dashnex 0.6.0 → 0.7.0
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 +4 -2
- package/dist/chunks/auth-ezwXxi-m.js +1 -0
- package/dist/chunks/dashnex-BWlu5keu.js +1 -0
- package/dist/cli.js +1 -1
- package/dist/client.js +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/server.js +1 -1
- package/dist/src/commands/dev.d.ts +16 -2
- package/dist/src/lib/certificates.d.ts +52 -0
- package/package.json +7 -3
- package/dist/chunks/auth-w3cAodiw.js +0 -1
- package/dist/chunks/dashnex-DmYHFrUA.js +0 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ 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
|
-
| `dev` | Run the development server (run in DashNex application folder) |
|
|
27
|
+
| `dev` | Run the development server (run in DashNex application folder); `-p/--port`, `-H/--host`, `--https` |
|
|
28
28
|
| `check` | Run build-time validations (run in DashNex application folder) |
|
|
29
29
|
| `install` | Install dependencies using pnpm or npm (run in DashNex application folder) |
|
|
30
30
|
| `app create` | Create a new DashNex application |
|
|
@@ -49,7 +49,9 @@ code you are shown, and paste it back into the CLI. Codes are valid for 10
|
|
|
49
49
|
minutes and can only be used once.
|
|
50
50
|
|
|
51
51
|
**dev**:
|
|
52
|
-
-
|
|
52
|
+
- `-p, --port <port>` — Port for the dev server (falls back to the `PORT` env var, then 3000)
|
|
53
|
+
- `-H, --host <host>` — Host/interface to bind, e.g. `0.0.0.0` when the app runs inside Docker (falls back to the `HOST` env var, then localhost). With `--https` the certificate also covers this host.
|
|
54
|
+
- `--https` — Serve over HTTPS with a locally trusted certificate. Uses `mkcert` from your PATH, or downloads it once into `~/.dashnex/mkcert`, installs the local CA (may prompt for your password) and writes `localhost.pem` + `localhost-key.pem` into the app's `.dashnex/certificates/` folder (never committed). Falls back to HTTP if that fails.
|
|
53
55
|
|
|
54
56
|
**app create**:
|
|
55
57
|
- `--subdomain <subdomain>` — Subdomain
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"chalk";import s from"fs-extra";import n from"path";var r=e=>e instanceof Error&&("ExitPromptError"===e.name||("AbortError"===e.name||("User force closed the prompt"===e.message||!!e.message.includes("prompt was closed")))),t="Command interrupted.",o=e=>{const s=e.toLowerCase();return s.includes("token")||s.includes("password")||"auth_code"===s},a=e=>{if(null===e||"object"!=typeof e)return e;if(Array.isArray(e))return e.map(a);const s={};for(const[n,r]of Object.entries(e))o(n)?s[n]="[REDACTED]":s[n]=a(r);return s},i=s=>{process.env.DEBUG&&console.log(e.dim(s))},c=(s,n)=>{if(process.env.DEBUG){const r=a(n);console.log(e.dim(`${s}: ${JSON.stringify(r,null,2)}`))}},p=s=>{process.env.DEBUG&&s instanceof Error&&(console.log(e.dim(`Error: ${s.message}`)),s.stack&&console.log(e.dim(`Stack: ${s.stack}`)))},l=()=>"qa"===process.env.APPLICATION_ENV?"https://api.qa.dashnex.com":"https://api.dashnex.com",u=()=>process.env.DASHNEX_BUSINESS_API_URL?process.env.DASHNEX_BUSINESS_API_URL.replace(/\/+$/,""):"qa"===process.env.APPLICATION_ENV?"https://api.business.qa.dashnex.com":"https://api.business.dashnex.com",d={qa:"https://app.qa.dashnex.com",live:"https://app.dashnex.com"},f={qa:"hacmrrpg1wnjv12oq22p8lfm",live:"paijq1vhr6kxnbm22wjoftep"},h=()=>process.env.DASHNEX_ID_URL?process.env.DASHNEX_ID_URL.replace(/\/+$/,""):d[process.env.APPLICATION_ENV||"live"]||d.live,m=()=>process.env.DASHNEX_OAUTH_CLIENT_ID?process.env.DASHNEX_OAUTH_CLIENT_ID:f[process.env.APPLICATION_ENV||"live"]??"",v={qa:"https://3atfynmu2w.us-east-1.awsapprunner.com",live:"https://rkwbzvhcwh.us-east-1.awsapprunner.com"},k=()=>v[process.env.APPLICATION_ENV||"live"]||v.live,E=async(e,s={})=>{const n=await fetch(e,{...s,headers:{"Content-Type":"application/json",...s.headers}});return{response:n,body:await n.json().catch(()=>({}))}},N=process.env.DASHNEX_DIR||".dashnex",T=e=>n.join(e,N,"settings.json"),g=()=>{console.error(e.red("Please run 'dashnex login' to authenticate.")),console.error(e.green(`If you don't have an account yet, create one at ${h()}`))},w=e=>Boolean(e.idToken),x=async e=>{let r=n.resolve(e);for(;;){const e=T(r);if(await s.pathExists(e))return e;const t=n.dirname(r);if(t===r)return null;r=t}},y=e=>{try{const s=e.split(".");if(3!==s.length)return null;const n=JSON.parse(Buffer.from(s[1],"base64url").toString());return void 0!==n.id?String(n.id):null}catch{return null}},A=(e,s=30)=>{try{const n=e.split(".");if(3!==n.length)return!1;const r=JSON.parse(Buffer.from(n[1],"base64url").toString());if("number"!=typeof r.exp)return!1;const t=Math.floor(Date.now()/1e3),o=r.exp-s<=t;return o&&i(`Token expired or expiring soon (exp: ${r.exp}, now: ${t})`),o}catch{return!1}},S=async(e,n,r)=>{const t=w(r)?`${u()}/business/v1/token/refresh`:`${l()}/auth/v1/token/refresh`;i(`POST ${t}`);const{response:o,body:a}=await E(t,{method:"POST",body:JSON.stringify({refreshToken:e})});if(i(`Response: ${o.status}`),c("Refresh response",a),!o.ok)return i("Token refresh failed"),null;const p=a;if(!p.token||!p.refreshToken)return i("Refresh response missing tokens"),null;const d=await s.readJson(n);return d.token=p.token,d.refreshToken=p.refreshToken,await s.writeJson(n,d,{spaces:2}),i("Updated settings.json with new tokens"),_(p.token,p.refreshToken,n,{...d,token:p.token,refreshToken:p.refreshToken})},_=async(e,s,n,r)=>{if(A(e)){if(!s)return i("Token expired, no refreshToken available"),null;i("Token expired, refreshing proactively");try{return await S(s,n,r)}catch(o){return p(o),null}}const t=w(r)?`${u()}/business/v1/`:`${l()}/users/v1/`;i(`GET ${t}`);try{const{response:o,body:a}=await E(t,{headers:{Authorization:`Bearer ${e}`}});if(i(`Response: ${o.status}`),c("Validation response",a),o.ok){const s=a,n=s.name??s.companyName,t=r.businessId??(void 0!==s.id?String(s.id):void 0);if("string"==typeof n&&void 0!==t)return i(`Token valid, Already logged in as ${n}`),{token:e,businessId:String(t),userName:n}}return s?(i("Validation endpoint rejected token, refreshing"),await S(s,n,r)):(i("Token invalid, no refreshToken available"),null)}catch(o){return p(o),null}},I=async(e=process.cwd())=>{const n=await x(e);if(!n)return!1;try{return w(await s.readJson(n))}catch{return!1}},b=async(n={})=>{const{exitOnFailure:r=!0,dashnexPath:t}=n,o=process.env.DASHNEX_TOKEN;if(o)return A(o)?(i("DASHNEX_TOKEN is expired"),console.error(e.red("DASHNEX_TOKEN is expired. Provide a freshly issued token.")),r&&process.exit(1),null):(i("Using DASHNEX_TOKEN from environment"),{token:o,businessId:y(o)??""});const a=t??await x(process.cwd());if(!a||!(await s.pathExists(a)))return i(".dashnex/settings.json not found"),r&&(g(),process.exit(1)),null;let c;i(`Found settings at ${a}`);try{c=await s.readJson(a)}catch{return i("settings.json parse failed"),r&&(g(),process.exit(1)),null}const{token:p,refreshToken:l,businessId:u}=c;if(!p)return i("settings.json missing token"),r&&(g(),process.exit(1)),null;const d=await _(p,l,a,c);return d||(r&&(g(),process.exit(1)),null)};export{E as a,h as c,i as d,p as f,r as h,T as i,k as l,t as m,I as n,l as o,c as p,N as r,u as s,b as t,m as u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={$schema:"https://json.schemastore.org/package",name:"@dashnex/cli",version:"0.7.0",dashnex:{minAppVersion:"1.1.0"},description:"Command-line interface for DashNex framework",homepage:"https://dashnex.io",type:"module",bin:{dashnex:"bin/dashnex"},engines:{node:">=22"},exports:{".":{types:"./dist/server.d.ts",default:"./dist/server.js"},"./client":{types:"./dist/client.d.ts",default:"./dist/client.js"}},scripts:{build:"vite build",dev:"vite build --watch --mode development",test:"vitest run"},keywords:["dashnex","cli"],author:"Dashnex Team",license:"UNLICENSED",dependencies:{"adm-zip":"^0.5.17",chalk:"^5.6.2",commander:"^14.0.3",diff:"^8.0.4",dotenv:"^17.4.2","fs-extra":"^11.3.5",ignore:"^7.0.5",inquirer:"^13.4.3",jiti:"^2.7.0",partysocket:"^1.3.0",ws:"^8.21.0"},devDependencies:{"@dashnex/types":"^1.5.0","@types/adm-zip":"^0.5.8","@types/fs-extra":"^11.0.4","@types/inquirer":"^9.0.9","@types/node":"^25.7.0","@types/ws":"^8.18.1",eslint:"^9.39.4","rollup-plugin-preserve-directives":"^0.4.0",terser:"^5.47.1",typescript:"^5.9.3",vite:"^8.0.8","vite-plugin-dts":"5.0.0-beta.6",vitest:"^4.1.6"},publishConfig:{registry:"https://npm.dashnex.com/"},files:["dist","bin","AGENTS.md","CLAUDE.md"]},s={};export{e as n,s as t};
|
package/dist/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{h as n,m as o,s,t as e}from"./chunks/auth-
|
|
2
|
+
import{h as n,m as o,s,t as e}from"./chunks/auth-ezwXxi-m.js";import{Command as t}from"commander";import i from"chalk";import a from"fs-extra";import c from"path";import{fileURLToPath as r,pathToFileURL as d}from"url";import{execSync as l}from"child_process";import{createJiti as m}from"jiti";import p from"dotenv";var f=new t,u=c.dirname(r(import.meta.url)),{version:h,description:g}=JSON.parse(a.readFileSync(c.resolve(u,"..","package.json"),"utf8"));f.name("dashnex").description(g).version(h).option("--ci","Disable spinner and interactive prompts (CI mode)").arguments("[command...]").action(n=>{if(n&&n.length>0){const o=n[0];if(!f.commands.map(n=>n.name()).includes(o)){console.error(i.red(`Unknown command: ${o}`));const n=process.cwd(),s=a.existsSync(c.join(n,"dashnex.json")),e=a.existsSync(c.join(n,"node_modules"));["build","router","bindings","env","db"].includes(o)?s&&!e?console.log(i.yellow(`\`${o}\` is provided by installed modules. Run \`dashnex install\` first.`)):s?console.log(i.green("Run `dashnex` to see available commands.")):console.log(i.yellow(`\`${o}\` must be run inside a DashNex application folder (containing dashnex.json).`)):console.log(i.green("Run `dashnex` to see available commands.")),process.exit(1)}}!function(n){const o=[];o.push("Usage: dashnex [options] [command]"),o.push(""),o.push(n.description()),o.push(""),o.push("Options:"),n.options.forEach(n=>{const s=n.flags.padEnd(20);o.push(` ${s} ${n.description}`)}),o.push(""),o.push("Commands:"),n.commands.forEach(n=>{const s=` ${n.name().padEnd(20)} ${n.description()}`;o.push(s),n.commands.length>0&&n.commands.forEach(n=>{const s=` ${n.name().padEnd(20)} ${n.description()}`;o.push(s)})}),console.log(o.join("\n"))}(f)});var y=null;var x=null;async function j(n,o){const s=[];try{const e=function(n,o){const s=c.join(o,"package.json");if(a.existsSync(s))try{const e=JSON.parse(a.readFileSync(s,"utf8"));if(e.name===n)return o;const t={...e.dependencies,...e.devDependencies}[n];if(t&&(t.startsWith("link:")||t.startsWith("file:"))){const n=c.resolve(o,t.replace(/^(link:|file:)/,""));if(a.existsSync(n))return n}}catch{}const e=c.join(o,"node_modules",n);if(a.existsSync(e))return a.realpathSync(e);if(y){const o=c.join(y,n);if(a.existsSync(o))return o}return e}(n,o),t=[c.join(e,"src","commands","index.ts"),c.join(e,"src","commands","index.js")],i=c.join(e,"dist","commands","index.js"),r=(function(n,o){const s=c.join(o,"package.json");if(!a.existsSync(s))return!1;try{const o=JSON.parse(a.readFileSync(s,"utf8")),e={...o.dependencies,...o.devDependencies}[n];return"string"==typeof e&&(e.startsWith("link:")||e.startsWith("file:"))}catch{return!1}}(n,o)?[...t,i]:[i,...t]).find(n=>a.existsSync(n));if(r){process.env.DEBUG&&console.log(`Loading ${n} commands from: ${r}`);const o=await async function(n){return/\.(ts|mts|cts|tsx)$/.test(n)?(x??=m(import.meta.url,{interopDefault:!1}),await x.import(n)):await import(d(n).href)}(r);let e=[];Array.isArray(o)?e=o:o.default?e=Array.isArray(o.default)?o.default:[]:o.commands&&(e=Array.isArray(o.commands)?o.commands:[]),e.length>0&&s.push(...e)}}catch(e){if(process.env.DEBUG){const o=e instanceof Error?e.message:String(e);console.log(i.yellow(`Module ${n} has no commands or failed to load: ${o}`))}}return s}function S(t,a,c={}){const r=t.command(a.name).description(a.description);a.arguments&&a.arguments.forEach(n=>{const o=n.required?`<${n.name}>`:`[${n.name}]`;void 0!==n.defaultValue?r.argument(o,n.description,n.defaultValue):r.argument(o,n.description)}),r.option("--ci","Disable spinner (CI mode)"),a.options&&a.options.forEach(n=>{void 0!==n.defaultValue?"number"==typeof n.defaultValue?r.option(n.flags,n.description,String(n.defaultValue)):r.option(n.flags,n.description,n.defaultValue):r.option(n.flags,n.description)}),a.subcommands&&a.subcommands.forEach(n=>{S(r,n,{...c,...a.options})}),a.subcommands&&a.subcommands.length>0?r.action(async()=>{r.outputHelp()}):r.action(async(...t)=>{try{const n=t[t.length-1],o=n.opts(),i=n.processedArgs,r={...c,...o};a.arguments?.forEach((n,o)=>{void 0!==i[o]&&(r[n.name]=i[o])}),r.context={auth:{getSession:()=>e()},businessApiBase:s()},await a.handler.execute(r)}catch(r){if(n(r))return void console.log(i.yellow(o));console.error(i.red(`❌ Command failed in ${a.name}:`),r),process.exit(1)}})}(async function(){p.config({quiet:!0}),process.env.DASHNEX_CLI="1",function(){try{y=l("npm root -g",{encoding:"utf8"}).trim()}catch{y=null}}(),process.argv.includes("--ci")&&(process.env.DASHNEX_CI="1");let n=process.cwd();const o=c.dirname(r(import.meta.url)),s=c.resolve(o,"..");process.env.DEBUG&&(console.log(`CLI started from directory: ${n}`),console.log(`CLI package directory: ${s}`),console.log(`Global node_modules: ${y??"not found"}`));const e=await async function(n){try{const s=[],e=/* @__PURE__ */new Set,t=n=>{e.has(n)||(e.add(n),s.push(n))},i=c.join(n,"dashnex.json");if(a.existsSync(i)){const o=c.join(n,"package.json");if(a.existsSync(o)){const n=JSON.parse(a.readFileSync(o,"utf8"));n.name&&t(n.name)}}const r=c.join(n,"package.json");if(a.existsSync(r)){const o=JSON.parse(a.readFileSync(r,"utf8")),s={...o.dependencies,...o.devDependencies,...o.optionalDependencies};for(const[e,i]of Object.entries(s)){if("string"==typeof i&&(i.startsWith("link:")||i.startsWith("file:"))){const o=c.resolve(n,i.replace(/^(link:|file:)/,""));if(a.existsSync(c.join(o,"dashnex.json"))){t(e);continue}}const o=c.join(n,"node_modules",e);a.existsSync(c.join(o,"dashnex.json"))&&t(e)}}if(y&&a.existsSync(y))try{const n=a.readdirSync(y);for(const o of n)if(o.startsWith("@")){const n=c.join(y,o);try{if(a.statSync(n).isDirectory()){const s=a.readdirSync(n);for(const n of s){const s=`${o}/${n}`,e=c.join(y,s);a.existsSync(c.join(e,"dashnex.json"))&&t(s)}}}catch{}}else{const n=c.join(y,o);a.existsSync(c.join(n,"dashnex.json"))&&t(o)}}catch(o){if(process.env.DEBUG){const n=o instanceof Error?o.message:String(o);console.log("⚠ Error scanning global node_modules:",n)}}return s}catch(o){if(process.env.DEBUG){const n=o instanceof Error?o.message:String(o);console.log("⚠ Error reading app package.json:",n)}return[]}}(n);e.includes("@dashnex/cli")||e.push("@dashnex/cli"),process.env.DEBUG&&(console.log(`CLI package directory: ${s}`),console.log(`Discovered modules: ${e.join(", ")}`));for(const r of e){let o=[];if("@dashnex/cli"===r)try{const n=c.join(s,"dist","commands","index.js");if(process.env.DEBUG&&console.log(`Loading @dashnex/cli commands from: ${n}`),await a.pathExists(n)){const s=await import(d(n).href);s.default?o=Array.isArray(s.default)?s.default:[]:s.commands&&(o=Array.isArray(s.commands)?s.commands:[]),process.env.DEBUG&&console.log(`Loaded ${o.length} commands from @dashnex/cli`)}}catch(m){if(process.env.DEBUG){const n=m instanceof Error?m.message:String(m);console.log(i.yellow(`Failed to load @dashnex/cli commands: ${n}`))}}else o=await j(r,n);for(const n of o)S(f,n)}const t=c.join(n,"dist","commands","index.js");if(await a.pathExists(t))try{const{default:n}=await import(d(t).href);if(n&&Array.isArray(n))for(const o of n)S(f,o)}catch(m){if(process.env.DEBUG){const n=m instanceof Error?m.message:String(m);console.error(i.yellow(`⚠ Failed to load application commands: ${n}`))}}f.parse(process.argv)})().catch(s=>{n(s)&&(console.log(i.yellow(o)),process.exit(0)),console.error(i.red("❌ CLI failed:"),s),process.exit(1)});
|
package/dist/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as s,t as e}from"./chunks/dashnex-
|
|
1
|
+
import{n as s,t as e}from"./chunks/dashnex-BWlu5keu.js";var n={name:s.name,version:s.version,description:s.description,...e};export{n as default};
|
package/dist/commands/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,c as t,d as o,f as s,h as n,i as r,l as a,m as i,n as c,o as l,p as d,r as p,s as u,t as h,u as g}from"../chunks/auth-w3cAodiw.js";import m from"chalk";import f from"fs-extra";import y from"path";import{fileURLToPath as w}from"url";import{spawn as $}from"child_process";import v from"fs";import k from"inquirer";import{createHash as x,randomBytes as b}from"crypto";import S from"os";import E from"adm-zip";import{diffLines as D}from"diff";import C from"ignore";import{WebSocket as T}from"partysocket";import P from"ws";import{inspect as N}from"node:util";import{rm as A}from"node:fs/promises";var j=e=>e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,""),I=async e=>{const t=(e=>{switch(process.platform){case"darwin":return{command:"open",args:[e]};case"win32":return{command:"cmd",args:["/c","start","",e]};default:return{command:"xdg-open",args:[e]}}})(e);return!!t&&new Promise(e=>{try{const n=$(t.command,t.args,{stdio:"ignore",detached:!0});n.on("error",t=>{s(t),e(!1)}),n.on("spawn",()=>{o(`Opened browser via ${t.command}`),n.unref(),e(!0)})}catch(n){s(n),e(!1)}})},R="urn:ietf:wg:oauth:2.0:oob",F=class extends Error{constructor(e,t){super(e),this.code=t,this.name="OAuthError"}},O=class extends Error{},B=class extends O{},z=e=>{const t=e.data;return t&&"object"==typeof t?t:e},L=e=>{const t=e.items??[];return Array.isArray(t)?t.map(e=>{if(!e||"object"!=typeof e)return null;const t=e,o=Number(t.id);return!Number.isFinite(o)||o<=0?null:{id:o,name:String(t.name??t.companyName??`Business ${o}`).trim()}}).filter(e=>null!==e):[]},_=async(s,n)=>{const r=[];let a=1,i=0;do{const c=`${t()}${s}?page=${a}&perPage=100`;o(`GET ${c}`);const{response:l,body:p}=await e(c,{headers:{Authorization:`Bearer ${n}`}});if(o(`Response: ${l.status}`),d(`Businesses response (${s})`,p),!l.ok){if(401===l.status)throw new B("Your DashNex ID session has expired.");if(404===l.status)throw new O(`Your DashNex ID account is not linked to a contact yet. Finish signing up at ${t()} and try again.`);throw new O("Failed to fetch businesses. Please try again.")}const u=z(p),h=L(u);if(i="number"==typeof u.total?u.total:h.length,r.push(...h),h.length<100||r.length>=i)break;a++}while(r.length<i);return r},U=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],M=(e="")=>{if(process.env.DASHNEX_CI)return{start:()=>{},stop:()=>{},text:()=>{}};let t=null,o=0,s=e;const n=()=>{const e=U[o%U.length];process.stderr.write(`\r[K${e} ${s}`),o++};return{start:e=>{void 0!==e&&(s=e),t||(o=0,n(),t=setInterval(n,80))},stop:()=>{t&&(clearInterval(t),t=null),process.stderr.write("\r[K\n")},text:e=>{s=e,t&&n()}}},W=class a{async execute(e={}){o("Login flow started");const t=r(process.cwd()),a=await h({exitOnFailure:!1,dashnexPath:t});if(a)console.log(m.green(`Already logged in as ${a.userName??"user"}`));else{g()||(console.error(m.red("No DashNex ID OAuth client is configured for this environment.")),console.error(m.yellow("Set DASHNEX_OAUTH_CLIENT_ID, or use APPLICATION_ENV=qa.")),process.exit(1));try{const s=await this.readSavedIdToken(t);if(s){o("Resuming with saved DashNex ID session");try{return void(await this.selectBusiness(s,t,e.business))}catch(c){if(!(c instanceof B))throw c;o("Saved DashNex ID session rejected, starting a new sign-in")}}const n=await this.authorize(!1!==e.browser);await this.writeConfig(t,{idToken:n.token,...n.refreshToken&&{idRefreshToken:n.refreshToken}}),await this.selectBusiness(n.token,t,e.business)}catch(c){if(s(c),n(c))return void console.log(m.yellow(i));if((c instanceof F||c instanceof O)&&(console.error(m.red(c.message)),process.exit(1)),c instanceof Error&&c.message.startsWith("EXIT:"))throw c;console.error(m.red("Could not reach DashNex. Check your connection and try again.")),process.exit(1)}}}async authorize(s){const{verifier:n,challenge:r}=(()=>{const e=j(b(32));return{verifier:e,challenge:j(x("sha256").update(e).digest())}})(),a=(e=>{const o=new URL(`${t()}/api/oauth/authorize`);return o.searchParams.set("client_id",g()),o.searchParams.set("redirect_uri",R),o.searchParams.set("response_type","code"),o.searchParams.set("code_challenge",e),o.searchParams.set("code_challenge_method","S256"),o.toString()})(r);o(`Authorize URL: ${a}`);const i=!!s&&await I(a);console.log(""),i?(console.log(m.green("Opened your browser to finish signing in.")),console.log(m.dim("If nothing opened, use this URL:"))):console.log(m.bold("Open this URL to sign in:")),console.log(m.cyan(a)),console.log(""),console.log(m.dim("Approve the request, then copy the code shown and paste it below.")),console.log("");const{code:c}=await k.prompt([{type:"input",name:"code",message:"Paste the code:",validate:e=>!!e.trim()||"Code is required"}]),l=M();l.start("Verifying code...");try{return await(async(s,n)=>{const r=`${t()}/api/oauth/token`;o(`POST ${r}`);const{response:a,body:i}=await e(r,{method:"POST",body:JSON.stringify({grant_type:"authorization_code",client_id:g(),redirect_uri:R,code:s.trim(),code_verifier:n})});if(o(`Response: ${a.status}`),d("Token response",i),!a.ok){const e="string"==typeof i.error?i.error:void 0,t="string"==typeof i.error_description?i.error_description:void 0;if("invalid_grant"===e)throw new F("That code is not valid anymore. Codes expire 10 minutes after they are issued and can only be used once.",e);throw new F(t??"Could not exchange the code for a token.",e)}const c=i;if(!c.access_token)throw new F("The authorization server did not return an access token.");return{token:c.access_token,refreshToken:c.refresh_token,expiresIn:c.expires_in}})(c,n)}finally{l.stop()}}async selectBusiness(s,n,r){const a=M();let i;a.start("Fetching businesses...");try{i=await(async e=>{const t=await _("/api/businesses/my",e),s=await _("/api/businesses/other",e),n=/* @__PURE__ */new Map;for(const o of t)n.set(o.id,o);for(const o of s)n.has(o.id)||n.set(o.id,o);return o(`Businesses (my: ${t.length}, other: ${s.length}, merged: ${n.size})`),[...n.values()]})(s)}finally{a.stop()}0===i.length&&(console.error(m.red("No businesses or teams found for your account!")),console.log(m.green(`Create your first business at ${t()}`)),process.exit(1));const c=await this.pickBusiness(i,r);let l;a.start("Switching business...");try{l=await(async(s,n)=>{const r=`${t()}/api/businesses/${n}/switch`;o(`POST ${r}`);const{response:a,body:i}=await e(r,{method:"POST",headers:{Authorization:`Bearer ${s}`}});if(o(`Response: ${a.status}`),d("Switch response",i),!a.ok){if(401===a.status)throw new B("Your DashNex ID session has expired.");if(403===a.status)throw new O("You do not have access to that business.");throw new O("Failed to switch to business. Please try again.")}const c=z(i);if(!c.token||!c.refreshToken)throw new O("Invalid response from server. Please try again.");return{token:c.token,refreshToken:c.refreshToken,businessId:n}})(s,c.id)}finally{a.stop()}const p={token:l.token,refreshToken:l.refreshToken,businessId:l.businessId,...await this.fetchCliSettings(l.token)};await this.writeConfig(n,p),await this.ensureGitignore(),console.log(m.green(`Logged in as ${c.name}`))}async pickBusiness(e,t){if(t){const s=e.find(e=>String(e.id)===String(t).trim());return s||(console.error(m.red(`Business "${t}" not found.`)),process.exit(1)),o(`Selected business via flag: ${s.id} ${s.name}`),s}if(1===e.length)return o(`Selected business: ${e[0].id} ${e[0].name}`),e[0];const{businessId:s}=await k.prompt([{type:"select",name:"businessId",message:"Select business:",choices:e.map(e=>({name:e.name,value:e.id}))}]),n=e.find(e=>e.id===s)||e[0];return o(`Selected business: ${n.id} ${n.name}`),n}async readSavedIdToken(e){if(!(await f.pathExists(e)))return null;try{return(await f.readJson(e)).idToken??null}catch{return null}}async fetchCliSettings(t){try{const s=`${u()}/business/v1/cli/settings`;o(`GET ${s}`);const{response:n,body:r}=await e(s,{headers:{Authorization:`Bearer ${t}`}});if(o(`Response: ${n.status}`),d("CLI settings response",r),n.ok&&r&&"object"==typeof r)return r}catch(n){s(n)}return{}}static{this.OBSOLETE_KEYS=["issuer"]}async writeConfig(e,t){const s=y.dirname(e);await f.pathExists(s)&&(await f.stat(s)).isFile()&&await f.remove(s),await f.ensureDir(s);let n=t;if(await f.pathExists(e))try{n={...await f.readJson(e),...t};for(const e of a.OBSOLETE_KEYS)delete n[e]}catch{o("Existing settings.json could not be parsed, overwriting")}await f.writeJson(e,n,{spaces:2}),o("Saved credentials to .dashnex/settings.json")}async ensureGitignore(){const e=y.join(process.cwd(),".gitignore");if(!(await f.pathExists(e)))return;let t=await f.readFile(e,"utf8");t.includes(".dashnex")||(t=t.trimEnd(),t.endsWith("\n")||(t+="\n"),t+="\n.dashnex\n",await f.writeFile(e,t))}},J=["Using vars defined in \\.env","\\(node:\\d+\\) MaxListenersExceededWarning:","\\(Use `node --trace-warnings","\\[WARNING\\].*You have defined bindings to the following internal Durable Objects","You have defined bindings to the following internal Durable Objects",'\\{"name":".+","class_name":".+"\\}',"These will not work in local development, but they should work in production","If you want to develop these locally, you can define your DO in a separate Worker","For detailed instructions, refer to the Durable Objects section here:","developers.cloudflare.com/workers/wrangler/api#supported-bindings","workerd/server/server\\.c\\+\\+.*warning:.*DurableObjectNamespace"],G=(e,t)=>t.length>0&&t.some(t=>t.test(e)),X=(e,t)=>{let s="";return{write:n=>{const r="string"==typeof n?n:n.toString();s+=r;const a=s.split(/\r?\n/);s=a.pop()??"";for(const s of a){const n=s.trim();n?G(n,t)?o(`Filtered: ${n}`):e.write(s+"\n"):o("Filtered: <empty line>")}},flush:()=>{const n=s.trim();if(n){if(G(n,t))return o(`Filtered: ${n}`),void(s="");e.write(s),s=""}else s=""}}},q=async(e,t={})=>{const n=y.join(e,"dashnex.json"),r=y.join(e,"package.json");if(o(`Checking for dashnex.json at: ${n}`),!(await f.pathExists(n)))throw new Error("Run this command in a DashNex Web application folder.");if(o("dashnex.json found, checking for package.json"),!(await f.pathExists(r)))throw new Error("package.json is missing.");let a;o("package.json found, checking for dev script");try{a=await f.readJson(r)}catch(l){throw s(l),new Error("Failed to read package.json.")}if(!a.scripts||!a.scripts.dev)throw new Error('The "dev" script is missing from package.json. Please add a "dev" script to package.json.');o("dev script found, checking for npm availability");const i=await(async e=>new Promise(t=>{const o=$(`${e} --version`,{stdio:"ignore",shell:!0});o.on("error",()=>{t(!1)}),o.on("close",e=>{t(0===e)})}))("npm");if(o(`npm available: ${i}`),!i)throw new Error("npm is not available. Please install npm to continue.");const c=t.https?["--","--experimental-https"]:[];return o(`npm available, executing: npm run dev in ${e}${t.https?" -- --experimental-https":""}`),(async(e,t=[],o=process.cwd(),n=[])=>{const r=t.length?`${e} ${t.join(" ")}`:e,a=n.length>0;return new Promise((e,t)=>{const i=$(r,{stdio:a?["inherit","pipe","pipe"]:"inherit",cwd:o,shell:!0,env:{...process.env,NODE_ENV:"development",FORCE_COLOR:"1"}});if(i.on("error",e=>{s(e),t(e)}),a&&i.stdout&&i.stderr){const t=X(process.stdout,n),o=X(process.stderr,n);i.stdout.on("data",e=>t.write(e)),i.stderr.on("data",e=>o.write(e)),i.on("close",s=>{t.flush(),o.flush(),e(null===s?0:s)})}else i.on("close",t=>{e(null===t?0:t)})})})("npm run dev",c,e,(()=>{const e=[];for(const t of J)try{e.push(new RegExp(t))}catch{o(`Invalid dev output filter pattern ignored: ${t}`)}return e})())},Y=async e=>new Promise(t=>{const o=$(`${e} --version`,{stdio:"ignore",shell:!0});o.on("error",()=>{t(!1)}),o.on("close",e=>{t(0===e)})}),H=async e=>{let t;if(await Y("pnpm"))t="pnpm install --config.strict-dep-builds=false",o("Using package manager: pnpm");else{if(!(await Y("npm")))throw new Error("Neither pnpm nor npm is available. Install Node.js (which bundles npm) or pnpm to continue.");t="npm install --legacy-peer-deps",o("Using package manager: npm (with --legacy-peer-deps for DashNex peer-dep overlap)")}return o(`Executing: ${t} in ${e}`),new Promise((o,n)=>{const r=[],a=[],i=$(t,{stdio:["ignore","pipe","pipe"],cwd:e,shell:!0,env:{...process.env,NPM_CONFIG_LOGLEVEL:"error"}});i.stdout&&i.stdout.on("data",e=>r.push(e)),i.stderr&&i.stderr.on("data",e=>a.push(e)),i.on("error",e=>{s(e),n(e)}),i.on("close",e=>{const t=null===e?0:e;0!==t&&(r.length&&process.stdout.write(Buffer.concat(r)),a.length&&process.stderr.write(Buffer.concat(a))),o(t)})})},V="Run this command in a DashNex Web application folder.",K=e=>{const t=y.join(e,"dashnex.json");return f.pathExistsSync(t)},Z=(e,t=!1)=>{t||console.log(m.cyan(e))},Q=(e,t,n={})=>{const r=`${e} ${t.join(" ")}`,a=!0===n.silent;return new Promise((t,n)=>{o(`Running: ${r}`);const i=$(r,{stdio:a?["ignore","pipe","pipe"]:"inherit",cwd:process.cwd(),shell:!0});i.on("error",e=>{s(e),n(e)}),i.on("close",s=>{o(`${e} exited with code ${s}`),t(null===s?1:s)})})},ee=(e,t)=>new Promise((n,r)=>{o(`Running: ${e} ${t.join(" ")}`);const a=$(e,t,{cwd:process.cwd()});let i="";a.stdout?.on("data",e=>{i+=e.toString()}),a.stderr?.on("data",e=>{i+=e.toString()}),a.on("error",e=>{s(e),r(e)}),a.on("close",t=>{o(`${e} exited with code ${t}`),n({exitCode:null===t?1:t,output:i})})}),te=async e=>{o("Running check: install");try{return 0===await H(process.cwd())}catch(t){return s(t),!e&&t instanceof Error&&console.error(m.red(t.message)),!1}},oe=async()=>{try{const{output:e}=await ee(process.execPath,[process.argv[1],"db","migrate","--help"]);return e.includes("Usage: dashnex db migrate")}catch(e){return s(e),!1}},se=async e=>{o("Running check: db migrate");try{const{exitCode:t,output:s}=await ee(process.execPath,[process.argv[1],"db","migrate"]);return 0===t||(s.includes("Unknown command")?(o("db migrate command is not available, skipping"),!0):(!e&&s.trim().length>0&&process.stderr.write(s),!1))}catch(t){return s(t),!0}},ne=e=>new Promise(t=>{const o=$(`${e} --version`,{stdio:"ignore",shell:!0});o.on("error",()=>t(!1)),o.on("close",e=>t(0===e))}),re=[{name:"TypeScript",command:"npx",args:["tsc","--noEmit"],notFoundMessage:"TypeScript is not available. Please install it with: npm install -D typescript"}],ae=class{async execute(){o("Check flow started"),K(process.cwd())||(console.error(m.red(V)),process.exit(1)),Z("Checking dependencies..."),await te(!1)||(console.error(m.red("Check failed.")),process.exit(1)),o(`Running ${re.length} check(s)`);for(const t of re){o(`Running check: ${t.name}`),Z(`Checking ${t.name}...`),await ne(t.command)||(console.error(m.red(t.notFoundMessage)),process.exit(1));try{0!==await Q(t.command,t.args)&&(console.error(m.red("Check failed.")),process.exit(1))}catch(e){s(e),console.error(m.red(t.notFoundMessage)),process.exit(1)}}await oe()?(Z("Checking database migrations..."),await se(!1)||(console.error(m.red("Check failed.")),process.exit(1))):o("db migrate command is not available, skipping"),console.log(m.green("All checks passed."))}async run(e=!1){if(o("Running checks (called from push)"),!K(process.cwd()))return e||console.error(m.red(V)),!1;if(Z("Checking dependencies...",e),!(await te(e)))return!1;const t=e?{silent:!0}:void 0;for(const r of re){if(o(`Running check: ${r.name}`),Z(`Checking ${r.name}...`,e),!(await ne(r.command)))return console.error(m.red(r.notFoundMessage)),!1;try{if(0!==await Q(r.command,r.args,t))return!1}catch(n){return s(n),console.error(m.red(r.notFoundMessage)),!1}}if(await oe()){if(Z("Checking database migrations...",e),!(await se(e)))return!1}else o("db migrate command is not available, skipping");return e||console.log(m.green("All checks passed.")),!0}},ie=" │ ",ce=e=>e.subarray(0,8192).includes(0),le=e=>{const t=e.split("\n");return""===t[t.length-1]&&t.pop(),t},de=(e,t)=>{if(null===e)return" ".repeat(t);const o=e.replace(/\t/g," ");return o.length<=t?o.padEnd(t):`${o.slice(0,Math.max(0,t-1))}…`},pe=(e,t,o)=>{const s=Math.max(o??process.stdout.columns??120,43),n=Math.floor((s-3)/2),r=((e,t)=>{const o=[],s=D(e,t);for(let n=0;n<s.length;n+=1){const e=s[n];if(e.removed){const t=le(e.value),r=s[n+1],a=r?.added?le(r.value):[];r?.added&&(n+=1);const i=Math.max(t.length,a.length);for(let e=0;e<i;e+=1)o.push({left:t[e]??null,right:a[e]??null,changed:!0})}else if(e.added)for(const t of le(e.value))o.push({left:null,right:t,changed:!0});else for(const t of le(e.value))o.push({left:t,right:t,changed:!1})}return o})(e,t),a=/* @__PURE__ */new Set;r.forEach((e,t)=>{if(e.changed)for(let o=Math.max(0,t-3);o<=Math.min(r.length-1,t+3);o+=1)a.add(o)});const i=[];i.push(m.bold(de("local (current)",n))+m.dim(ie)+m.bold("incoming"));let c=-1,l=0,d=0;for(let p=0;p<r.length;p+=1){if(!a.has(p))continue;if(l>=200){d+=1;continue}-1!==c&&p>c+1&&i.push(m.dim("···")),c=p,l+=1;const e=r[p],t=de(e.left,n),o=de(e.right,n);if(!e.changed){i.push(m.dim(t)+m.dim(ie)+m.dim(o));continue}const s=null!==e.left?m.red(t):t,u=null!==e.right?m.green(o):o;i.push(s+m.dim(ie)+u)}return d>0&&i.push(m.dim(`… ${d} more diff line(s) not shown`)),i.join("\n")},ue=[".dashnex",".git",".vinext",".wrangler","wrangler.jsonc","wrangler.toml",".dev.vars",".github"],he=[".env","wrangler.jsonc"],ge=e=>y.join(e,p,"sync.json"),me=e=>x("sha1").update(`blob ${e.length}\0`).update(e).digest("hex"),fe=e=>e.split(y.sep).join("/"),ye=e=>ue.some(t=>e===t||e.startsWith(`${t}/`)),we=async e=>{const t=await(async e=>{const t=C();t.add(ue);const o=y.join(e,".gitignore");return await f.pathExists(o)&&t.add(await f.readFile(o,"utf8")),t})(e),o=[],s=async n=>{const r=await f.readdir(n,{withFileTypes:!0});for(const a of r){const r=y.join(n,a.name),i=fe(y.relative(e,r));ye(i)||t.ignores(a.isDirectory()?`${i}/`:i)||(a.isDirectory()?await s(r):a.isFile()&&o.push(i))}};return await s(e),o},$e=async(e,t={})=>{const o={},s=await we(e);for(const n of s)o[n]=me(await f.readFile(y.join(e,n)));if(t.includeGenerated)for(const n of he){if(n in o)continue;const t=y.join(e,n);(await f.lstat(t).catch(()=>null))?.isFile()&&(o[n]=me(await f.readFile(t)))}return o},ve=async e=>{try{const t=await f.readFile(ge(e),"utf8"),o=JSON.parse(t);return"string"!=typeof o?.commit||"object"!=typeof o?.manifest||null===o.manifest?null:o}catch{return null}},ke=async(e,t,s)=>{const n={commit:t,manifest:s,updatedAt:/* @__PURE__ */(new Date).toISOString()},r=ge(e);try{await f.ensureDir(y.dirname(r)),await f.writeFile(r,`${JSON.stringify(n,null,2)}\n`),o(`Sync state written: commit ${t}, ${Object.keys(s).length} files`)}catch(a){o(`Failed to write sync state: ${a instanceof Error?a.message:"unknown"}`)}},xe=1048576,be=async(e,t)=>{const{install:n}=await k.prompt([{type:"confirm",name:"install",message:"Install dependencies?",default:!0}]);if(!n){if(y.resolve(e)!==y.resolve(t)){const o=y.relative(t,e);console.log(m.cyan(`Run "cd ${o}" to change to the application folder.`))}return}try{const t=M();t.start("Installing dependencies...");try{const s=await H(e);if(t.stop(),0!==s)return void o(`install exited with code ${s}`)}catch(i){throw t.stop(),i}}catch(i){return s(i),void console.error(m.red(i instanceof Error?i.message:"Failed to run install."))}const{migrate:r}=await k.prompt([{type:"confirm",name:"migrate",message:"Create or update database?",default:!0}]);if(!r)return;o(`Running dashnex db migrate in ${e}`);try{const t=M();t.start("Creating or updating database...");try{const n=await((e,t,o)=>{const n=`${y.join(e,"node_modules",".bin")}${y.delimiter}${process.env.PATH??""}`,r={...process.env,PATH:n};return new Promise((n,a)=>{const i=$(t,{cwd:e,shell:!0,stdio:"inherit",env:o?{...r,...o}:r});i.on("error",e=>{s(e),a(e)}),i.on("close",e=>{n(null===e?0:e)})})})(e,"dashnex db migrate");if(t.stop(),0!==n)return void o(`dashnex db migrate exited with code ${n}`)}catch(i){throw t.stop(),i}}catch(i){return s(i),void console.error(m.red("Failed to run dashnex db migrate."))}const{dev:a}=await k.prompt([{type:"confirm",name:"dev",message:"Run development server?",default:!0}]);if(a)try{await q(e)}catch(i){s(i),console.error(m.red(i instanceof Error?i.message:"Failed to run dev server."))}},Se=(e,t)=>{if(e.length!==t.length)return`local ${e.length} bytes, incoming ${t.length} bytes`;const o=e.length;for(let s=0;s<o;s+=1)if(e[s]!==t[s]){return`size ${o} matches; first diff at byte ${s} (local=0x${e[s].toString(16).padStart(2,"0")} incoming=0x${t[s].toString(16).padStart(2,"0")})`}return`size ${o} matches; no byte difference (unexpected)`},Ee=async(e,t)=>{let o;try{o=await f.lstat(e)}catch{return{kind:"new"}}if(o.isDirectory())return{kind:"different",reason:"target is a directory"};if(o.size!==t.length)return{kind:"different",reason:`local ${o.size} bytes, incoming ${t.length} bytes`};const s=await f.readFile(e);return 0===Buffer.compare(s,t)?{kind:"identical"}:{kind:"different",reason:Se(s,t)}},De=async(e,t,o)=>{console.log(`\n${m.bold(t)}`);try{const t=await f.readFile(e);if(t.length>xe||o.length>xe||ce(t)||ce(o))return void console.log(m.dim(`(no preview: ${Se(t,o)})`));console.log(pe(t.toString("utf8"),o.toString("utf8")))}catch(n){s(n),console.log(m.dim("(no preview available)"))}},Ce=async e=>{const{choice:t}=await k.prompt([{type:"expand",name:"choice",message:`Overwrite ${e}?`,default:"y",choices:[{key:"y",name:"yes (overwrite this file)",value:"yes"},{key:"n",name:"no (keep local version)",value:"no"},{key:"a",name:"overwrite all remaining differing files",value:"all"},{key:"s",name:"skip all remaining differing files",value:"skip"}]}]);return t},Te=async(e,t)=>{const o=t?m.yellow(" (has local changes!)"):"",{choice:s}=await k.prompt([{type:"expand",name:"choice",message:`Delete ${e}? It no longer exists in the application.${o}`,default:"y",choices:[{key:"y",name:"yes (delete this file)",value:"yes"},{key:"n",name:"no (keep this file)",value:"no"},{key:"a",name:"delete all remaining removed files",value:"all"},{key:"s",name:"keep all remaining removed files",value:"skip"}]}]);return s},Pe=async(e,t)=>{let o=y.dirname(e);const s=y.resolve(t);for(;y.resolve(o)!==s;)try{if((await f.readdir(o)).length>0)return;await f.rmdir(o),o=y.dirname(o)}catch{return}},Ne=(e,t)=>y.resolve(e)===y.resolve(t)?"Application is successfully pulled.":`Application is successfully pulled into ${y.relative(t,e)}.`,Ae=class{async execute(e={}){o("Pull flow started");const t=await h();if(!t)return;const r=process.cwd();let a;if(e.folder)a=e.folder.trim()||".",o(`Folder argument provided: ${a}`);else{o("No folder argument, prompting user");const{folder:e}=await k.prompt([{type:"input",name:"folder",message:"Which folder to pull the application to?",default:"."}]);a=e.trim()||"."}const c=y.resolve(r,a);o(`Target folder resolved: ${c}`);try{const e=await f.lstat(c).catch(()=>null);e&&!e.isDirectory()&&(console.error(m.red(`Cannot pull into ${c}: path exists and is not a directory.`)),process.exit(1)),await f.ensureDir(c),o(`Target folder ensured: ${c}`)}catch(p){s(p);const e=p instanceof Error?p.message:"Unknown error";console.error(m.red(`Cannot create folder ${c}: ${e}`)),process.exit(1)}const l=y.join(S.tmpdir(),`dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`),d=M();d.start("Analyzing local files...");try{const n=await ve(c),a=await $e(c,{includeGenerated:!0});o(`Local manifest has ${Object.keys(a).length} files`),d.text("Pulling application...");const i=await this.download(t.token,a);d.text("Downloading..."),await f.writeFile(l,i.buffer),o(`Zip written to temp file (${i.viaV2?"v2 diff":"legacy full"} pull)`),d.text("Extracting...");const u=new E(l).getEntries().filter(e=>!e.isDirectory);let h=null;const g=u.find(e=>".dashnex/pull.json"===e.entryName);if(g)try{h=JSON.parse(g.getData().toString("utf8"))}catch(p){s(p)}const w=u.filter(e=>".dashnex"!==e.entryName&&!e.entryName.startsWith(".dashnex/"));o(`Zip has ${w.length} file entries${h?`, meta commit ${h.commit}`:", no meta"}`);const $=e.yes||"1"===process.env.DASHNEX_CI;let v=$?"all":"prompt",k=!1,x=0,b=0,S=0,D=0,C=0;const T=async(e,t,o)=>{try{return await f.ensureDir(y.dirname(e)),await f.writeFile(e,t),o&&await f.chmod(e,o),!0}catch(p){return s(p),!1}};for(const e of w){const t=e.getData(),s=y.join(c,e.entryName),n=y.relative(c,s)||e.entryName,r=(e.header.attr??0)>>16&4095,a=await Ee(s,t);if(o(`Entry ${e.entryName}: ${a.kind}${a.reason?` (${a.reason})`:""}`),"identical"===a.kind){D+=1;continue}if("new"===a.kind){await T(s,t,r)?x+=1:C+=1;continue}let i=!1;if("all"===v)i=!0;else if("none"===v)i=!1;else{d.stop(),await De(s,n,t),k||(console.log(m.dim("Y = yes (default), n = skip, a = overwrite all, s = skip all, h = help")),k=!0);const r=await Ce(n);o(`Entry ${e.entryName}: user chose ${r}`),"yes"===r?i=!0:"no"===r?i=!1:"all"===r?(v="all",i=!0):(v="none",i=!1),d.start("Extracting...")}i?await T(s,t,r)?b+=1:C+=1:S+=1}d.stop();let P=0,N=0;if(h&&h.deleted.length>0){const e=await this.applyDeletions(c,h.deleted,n,$);P=e.deleted,N=e.kept}h&&(D+=Math.max(0,Object.keys(h.manifest).length-w.length)),o(`Pull summary: ${x} new, ${b} overwritten, ${S} skipped, ${P} deleted, ${D} unchanged, ${C} failed`);const A=[`${x} new`,`${b} overwritten`,`${S} skipped`,`${D} unchanged`];if((P>0||N>0)&&A.splice(3,0,`${P} deleted`),C>0&&A.push(`${C} failed`),console.log(m.cyan(`Pulled: ${A.join(", ")}`)),h){const e=await $e(c,{includeGenerated:!0});await ke(c,h.commit,e)}y.resolve(c)!==y.resolve(r)?(console.log(m.green(Ne(c,r))),process.chdir(c),o(`Changed working directory to ${c}`)):console.log(m.green(Ne(c,r))),C>0&&(console.error(m.red(`${C} file(s) failed to write. See DEBUG output for details.`)),process.exit(1)),$?o("Non-interactive mode (--yes or --ci), skipping post-pull child commands"):await be(c,r)}catch(p){if(d.stop(),s(p),p instanceof Error&&p.message.startsWith("EXIT:"))throw p;if(n(p))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}finally{await f.remove(l).catch(()=>{}),o("Temp zip removed")}}async download(e,t){const s=`${u()}/business/v1/cli/v2/pull`;o(`POST ${s}`);const n=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({manifest:t})});if(o(`v2 pull response: ${n.status}`),n.ok)return{buffer:Buffer.from(await n.arrayBuffer()),viaV2:!0};const r=await n.json().catch(()=>({}));var a,i;a=n.status,i=r,404===a&&"string"==typeof i.message&&i.message.startsWith("Cannot ")||this.failFromResponse(n.status,r),o("v2 pull endpoint unavailable, falling back to legacy pull");const c=`${u()}/business/v1/cli/pull`;o(`GET ${c}`);const l=await fetch(c,{headers:{Authorization:`Bearer ${e}`}});if(o(`Legacy pull response: ${l.status}`),!l.ok){const e=await l.json().catch(()=>({}));this.failFromResponse(l.status,e)}return{buffer:Buffer.from(await l.arrayBuffer()),viaV2:!1}}failFromResponse(e,t){let o="Failed to pull application.";if(401===e||403===e)o="Please run 'dashnex login' to authenticate.";else if(404===e)o="Business has no application. Run 'dashnex app create' to create one.";else{const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(t);e&&(o=e)}console.error(m.red(o)),process.exit(1)}async applyDeletions(e,t,n,r){let a=0,i=0;if(!n){const e=t.slice(0,20);console.log(m.yellow("These local files do not exist in the application (kept — no sync state yet):"));for(const t of e)console.log(m.yellow(` ${t}`));return t.length>e.length&&console.log(m.yellow(` … and ${t.length-e.length} more`)),{deleted:a,kept:t.length}}let c=r?"all":"prompt";for(const d of t){if(!(d in n.manifest)){o(`Deletion candidate ${d} was never synced, keeping`);continue}const t=y.join(e,d),r=await f.readFile(t).catch(()=>null);if(null===r)continue;const p=me(r)!==n.manifest[d];let u=!1;if("all"===c)u=!0;else if("none"===c)u=!1;else{const e=await Te(d,p);o(`Deletion ${d}: user chose ${e}`),"yes"===e?u=!0:"no"===e?u=!1:"all"===e?(c="all",u=!0):(c="none",u=!1)}if(u)try{await f.remove(t),await Pe(t,e),a+=1}catch(l){s(l),i+=1}else i+=1}return{deleted:a,kept:i}}},je=async e=>{const t=`${u()}/business/v1/agreements/payment-processing/status`;o(`GET ${t}`);const s=await fetch(t,{headers:{Authorization:`Bearer ${e}`}});if(o(`Payment agreement response: ${s.status}`),!s.ok)return!0;if((await s.json().catch(()=>({}))).hasAccepted)return!0;const n=`${await(async()=>"qa"===process.env.APPLICATION_ENV?"https://business.qa.dashnex.com":"https://business.dashnex.com")()}/money/agreement/`;return console.error(m.red(`You need to accept Master Payment Processing Agreement first by visiting this link: ${n}`)),!1},Ie=async(e,t={})=>{const n=t.ciMode??!1;return!!(await je(e))&&!!(await(async(e,t)=>{const n=`${u()}/business/v1/tos/latest`;o(`GET ${n}`);const r=await fetch(n,{headers:{Authorization:`Bearer ${e}`}});if(o(`TOS latest response: ${r.status}`),r.ok)return!0;if(404!==r.status)return!0;const a=`${u()}/business/v1/tos/current`;o(`GET ${a}`);const i=await fetch(a,{headers:{Authorization:`Bearer ${e}`}});if(o(`TOS current response: ${i.status}`),!i.ok)return o("Could not fetch current TOS, skipping check"),!0;const c=await i.json().catch(()=>({})),l=c.version??"unknown";if(c.text&&(console.log(m.dim("─".repeat(60))),console.log(m.bold(`Terms of Service ${l}`)),console.log(m.dim("─".repeat(60))),console.log(c.text.replace(/<br\s*\/?>/gi,"\n").replace(/<\/p>/gi,"\n\n").replace(/<\/div>/gi,"\n").replace(/<\/li>/gi,"\n").replace(/<[^>]+>/g,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/ /g," ").replace(/\n{3,}/g,"\n\n").trim()),console.log(m.dim("─".repeat(60)))),t)return console.log(m.yellow(`By proceeding you accept the Terms of Service ${l}.`)),!0;const{accept:d}=await k.prompt([{type:"confirm",name:"accept",message:`By creating an application you accept the Terms of Service ${l}. Continue?`,default:!0}]);if(!d)return console.log(m.yellow("Cancelled.")),!1;try{const t=`${u()}/business/v1/tos/${c.id}/accept`;o(`POST ${t}`),await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({})})}catch(p){s(p)}return!0})(e,n))},Re=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,Fe=e=>new Promise(t=>setTimeout(t,e)),Oe=e=>e.appFullCustomDomain?e.appFullCustomDomain:e.subdomain?"qa"===(process.env.APPLICATION_ENV??"")?`https://${e.subdomain}.dashnex.dev`:`https://${e.subdomain}.dashnexcloud.com`:null,Be=3e5,ze=99,Le=e=>new Promise(t=>setTimeout(t,e)),_e=async(e,t,n,r)=>{const a=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${t}`,Accept:"text/event-stream"}});if(o(`SSE connection opened: ${a.status}`),!a.ok){const e=a.status;let t="Failed to start deployment.";if(401===e||403===e)t="Please run 'dashnex login' to authenticate.";else try{const e=await a.text(),o=JSON.parse(e);"string"==typeof o.error?t=o.error:"string"==typeof o.message&&(t=o.message)}catch{}throw console.error(m.red(t)),new Error(`HTTP ${e}: ${t}`)}if(!a.body)throw new Error("Response body is null");o("SSE connection established successfully"),r?.();const i=a.body.getReader();try{for await(const e of async function*(e){const t=new TextDecoder("utf-8");let o="";for(;;){const{value:s,done:n}=await e.read();if(n)break;o+=t.decode(s,{stream:!0});const r=o.split("\n");o=r.pop()||"";let a="";for(const e of r)e.startsWith("data: ")?a=e.slice(6):""===e&&a&&(yield{data:a},a="")}if(o.startsWith("data: ")){const e=o.slice(6);e&&(yield{data:e})}}(i))try{if(!e.data){o("Received empty SSE event");continue}const t=JSON.parse(e.data);o(`SSE message received: ${JSON.stringify(t)}`),n(t)}catch(c){s(c),o(`Failed to parse SSE event data: ${e.data}`)}}finally{i.releaseLock()}},Ue=class{async execute(e={}){o("Deploy flow started");const t=await h();if(!t)return;if(!1!==e.check?(o("Running pre-deploy checks"),await(new ae).run(!1)||(console.error(m.red("Deploy aborted: checks failed. Re-run with --no-check to skip.")),process.exit(1))):o("Skipping pre-deploy checks (--no-check)"),!(await Ie(t.token,{ciMode:!0})))return;const r=`${a()}/business/v1/applications/deploy`;o(`GET ${r} (SSE)`);let c=0,l="",d=!1,p=0,u=!1;const g=M();let f=0,y=0,w=null;const $=(e,t)=>{const o=Math.round(e);g.text(`[${o}%] ${t}`)},v=()=>{k();const e=c,t=Date.now(),o=Be*((ze-e)/ze);w=setInterval(()=>{const s=Date.now()-t;let n;if(y>t){const e=Date.now()-y,t=ze-f,o=3030.3030303030305*t;n=Math.min(f+e/o*t,ze)}else n=Math.min(e+s/o*(ze-e),ze);n>c&&(c=n,$(c,l))},200)},k=()=>{w&&(clearInterval(w),w=null)};let x=!1;const b=e=>{if(u=!0,"string"==typeof e.error)return k(),g.stop(),console.error(m.red(e.error)),d=!0,void(x=!0);"string"==typeof e.message&&(l=e.message),"number"==typeof e.progress&&(f=e.progress,y=Date.now(),e.progress>c&&(c=e.progress)),"string"!=typeof e.message&&"number"!=typeof e.progress||(c>=100?(k(),g.stop(),d=!0):$(c,l))};for(;p<=5;)try{return u?$(c,l):g.start("Connecting..."),v(),await _e(r,t.token,b,()=>{u||(g.text(`[${Math.round(c)}%] Deployment in progress...`),l="Deployment in progress...")}),k(),g.stop(),o("SSE connection closed"),x&&process.exit(1),void(d&&await this.showDeployedUrl(t.token))}catch(S){if(k(),s(S),o(`SSE connection error: ${S}`),d)return g.stop(),void(x&&process.exit(1));if(n(S))return g.stop(),void console.log(m.yellow(i));S instanceof Error&&S.message.startsWith("HTTP")&&(g.stop(),console.error(m.red("Could not complete deployment. Check your connection and try again.")),process.exit(1)),p++,p>5&&(g.stop(),console.error(m.red("Deployment failed. Maximum reconnection attempts exceeded.")),process.exit(1));const e=Math.min(1e3*Math.pow(2,p-1),3e4);o(`Connection error. Reconnecting in ${e/1e3}s... (attempt ${p}/5)`),await Le(e)}}async showDeployedUrl(e){try{const t=`${u()}/business/v1/applications`;o(`GET ${t} (for deployed URL)`);const s=await fetch(t,{headers:{Authorization:`Bearer ${e}`}});if(!s.ok)return;const n=await s.json().catch(()=>({})),r=Oe({subdomain:n.subdomain,appFullCustomDomain:n.appFullCustomDomain});r&&console.log(`Application is deployed to ${m.cyan(r)}`)}catch(t){s(t)}}},Me=104857600,We=class extends Error{},Je=class extends Error{},Ge=class extends Error{},Xe="/business/v1/cli/tail-token";async function qe(e,t={}){const o=`${u()}${Xe}${t.debug?"?debug=1":""}`;try{await fetch(o,{method:"DELETE",headers:{Authorization:`Bearer ${e}`}})}catch{}}var Ye=["log","info","warn","error","debug"],He={error:4,warn:3,log:2,info:2,debug:1};function Ve(e){return"string"==typeof e&&Ye.includes(e)}function Ke(e){return e.map(e=>"string"==typeof e?e:function(e){try{return JSON.stringify(e)}catch{return String(e)}}(e)).join(" ")}function Ze(e){return e<10?`0${e}`:String(e)}function Qe(e){const t=new Date(e);return`${Ze(t.getHours())}:${Ze(t.getMinutes())}:${Ze(t.getSeconds())}.${o=t.getMilliseconds(),o<10?`00${o}`:o<100?`0${o}`:String(o)}`;var o}function et(e,t){if("string"==typeof e)return e;if("object"==typeof(o=e)&&null!==o&&"string"==typeof o.name&&"string"==typeof o.message&&Object.keys(o).every(e=>"name"===e||"message"===e||"stack"===e)){const t=`${e.name}: ${e.message}`;return e.stack?`${t}\n${e.stack}`:t}var o;return N(e,{colors:t,depth:2,breakLength:120})}var tt={error:e=>m.red(e),warn:e=>m.yellow(e),debug:e=>m.dim(e),log:e=>e,info:e=>e};function ot(e,t){const o=t.color?m.dim(Qe(e.ts)):Qe(e.ts),s=e.level.toUpperCase().padEnd(5),n=[o,t.color?tt[e.level](s):s];n.push(e.args.map(e=>et(e,t.color)).join(" "));let r=n.join(" ");if(e.truncated){const e=" …(truncated)";r+=t.color?m.dim(e):e}return r}function st(e,t,o){let s;try{s=JSON.parse(e)}catch{return{kind:"malformed",raw:e}}if("object"!=typeof s||null===s||!Ve(s.level))return{kind:"malformed",raw:e};const n=s;return Array.isArray(n.args)||(n.args=void 0===n.args?[]:[n.args]),r=n.level,a=t.minLevel,He[r]>=He[a]&&function(e,t){return!t||t.test(Ke(e.args))}(n,t.filter)?{kind:"line",line:ot(n,o)}:{kind:"filtered"};var r,a}var nt=new Set([4401,4403]);var rt=[{label:"node_modules",segments:["node_modules"],description:"installed dependencies"},{label:"node_modules/.vite",segments:["node_modules",".vite"],description:"vite cache"},{label:"node_modules/.vite-temp",segments:["node_modules",".vite-temp"],description:"vite cache"},{label:"src/dashnex",segments:["src","dashnex"],description:"generated module registry"},{label:"dist",segments:["dist"],description:"build output"}],at={label:".wrangler",segments:[".wrangler"],description:"local database"},it=async e=>{await A(e,{recursive:!0,force:!0,maxRetries:5,retryDelay:100})},ct={name:"app",description:"Manage DashNex applications (should be executed in application folder)",handler:{execute:async()=>{}},options:[],subcommands:[{name:"create",description:"Create a new DashNex application",handler:new class{async pollApplicationCreated(e,t,n=5e3,r=6e4){const a=Date.now();let i=0;for(;Date.now()-a<r;){i++,o(`Polling application status (attempt ${i})...`);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(c){s(c),o(`Poll attempt ${i} failed with network error`)}await Fe(n)}return{ready:!1}}async execute(e={}){o("Create flow started");const t=await h();if(!t)return;o("Auth valid, checking if business already has application");const r=M(),a=`${u()}/business/v1/applications`;o(`GET ${a}`),r.start("Checking application status...");try{const e=await fetch(a,{headers:{Authorization:`Bearer ${t.token}`}});r.stop(),o(`Applications response: ${e.status}`),o(`Applications response body: ${await e.text()}`),404!==e.status&&(console.error(m.red("Business already has an application. Run 'dashnex app pull' to pull the existing one.")),process.exit(1))}catch(w){if(r.stop(),s(w),w instanceof Error&&w.message.startsWith("EXIT:"))throw w;if(n(w))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}o("No existing application, checking agreements");const c=void 0!==e.subdomain;if(!(await Ie(t.token,{ciMode:c})))return;let l;o("Agreements checked, proceeding to subdomain prompt");const d=e=>`${u()}/business/v1/applications/check-subdomain?subdomain=${encodeURIComponent(e)}`;let p=e.subdomain?.trim();for(;;){if(p)l=p,p=void 0,o(`Subdomain provided via flag: ${l}`);else{if(c)return console.error(m.red("Subdomain is not available.")),void process.exit(1);{const{sub:e}=await k.prompt([{type:"input",name:"sub",message:"Pick your subdomain:",validate:e=>!!e.trim()||"Subdomain is required"}]);l=e.trim()}}o(`Checking subdomain: ${l}`);const e=d(l);o(`GET ${e}`),r.start("Checking subdomain...");try{const s=await fetch(e,{headers:{Authorization:`Bearer ${t.token}`}});r.stop(),o(`Check subdomain response: ${s.status}`);const n=await s.text();if(o(`Check subdomain response body: ${n}`),!s.ok){const e=s.headers.get("content-type")??"";let t="Failed to check subdomain availability.";if(401!==s.status&&403!==s.status||(t="Please run 'dashnex login' to authenticate.",console.error(m.red(t)),process.exit(1)),e.includes("application/json")){const e=JSON.parse(n);o(`Check subdomain error body: ${JSON.stringify(e)}`);const s=Re(e);s&&(t=s)}console.error(m.red(t));continue}const a=JSON.parse(n);if(o(`Check subdomain parsed body: ${JSON.stringify(a)}`),a.available){o(`Subdomain ${l} is available`);break}console.error(m.red("Subdomain is not available. Please choose a different one."))}catch(w){if(r.stop(),s(w),w instanceof Error&&w.message.startsWith("EXIT:"))throw w;if(n(w))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}const g=`${u()}/business/v1/cli/create`;o(`POST ${g}`);const f={subdomain:l};e.template&&(f.template=e.template,o(`Template: ${e.template}`));const y=JSON.stringify(f);o(`Create request body: ${y}`),r.start("Creating application...");try{const a=await fetch(g,{method:"POST",headers:{Authorization:`Bearer ${t.token}`,"Content-Type":"application/json"},body:y});r.stop(),o(`Create response: ${a.status}`);const l=await a.text();if(o(`Create response body: ${l}`),503===a.status){o("Create returned 503, polling for application creation..."),r.start("Waiting for application to be ready...");const e=`${u()}/business/v1/applications`,s=await this.pollApplicationCreated(e,t.token);r.stop(),s.ready||(console.error(m.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(m.red("Application creation failed. Please try again.")),process.exit(1)),o("Application found after polling")}else if(!a.ok){const e=a.headers.get("content-type")??"";let t="Failed to create application.";if(401===a.status||403===a.status)t="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e=JSON.parse(l);o(`Create error body parsed: ${JSON.stringify(e)}`);const s=Re(e);s&&(t=s)}console.error(m.red(t)),process.exit(1)}let d;if(o("Application created successfully"),!0===e.pull?(d=!0,o("--pull flag set, pulling without prompting")):!1===e.pull?(d=!1,o("--no-pull flag set, skipping pull")):c?(d=!1,o("CI mode (--subdomain), skipping pull")):(d=(await k.prompt([{type:"confirm",name:"pull",message:"Pull the application locally?",default:!0}])).pull,o(`User chose to pull: ${d}`)),d){o("Running pull flow");try{await(new Ae).execute()}catch(w){if(s(w),w instanceof Error&&w.message.startsWith("EXIT:"))throw w;if(n(w))return void console.log(m.yellow(i));console.error(m.red("Failed to pull application."))}}else console.log(m.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(w){if(r.stop(),s(w),w instanceof Error&&w.message.startsWith("EXIT:"))throw w;if(n(w))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[{flags:"-t, --template <template>",description:"Template (name@branch-or-tag)",defaultValue:"webapp-base@live"},{flags:"--subdomain <subdomain>",description:"Subdomain (CI mode, skip prompts)"},{flags:"--pull",description:"Auto-pull after creation (skip prompt)"},{flags:"--no-pull",description:"Skip the pull prompt and show the success message"}]},{name:"pull",description:"Pull existing application to specified folder",handler:new Ae,arguments:[{name:"folder",description:"Target folder (defaults to interactive prompt)"}],options:[{flags:"-y, --yes",description:"Skip prompts and child commands (CI mode)"}]},{name:"push",description:"Push local application to DashNex",handler:new class{async execute(e={}){o("Push flow started"),await f.pathExists(y.join(process.cwd(),"dashnex.json"))||(console.error(m.red("Not a DashNex application folder (dashnex.json not found).")),console.error(m.red("Run this command from your application root.")),process.exit(1)),!1!==e.check?(o("Running pre-push checks"),await(new ae).run(!1)||(console.error(m.red("Push aborted: checks failed. Re-run with --no-check to skip.")),process.exit(1))):o("Skipping pre-push checks (--no-check)");const t=await h();if(!t)return;const r=process.cwd(),a=M();try{a.start("Analyzing local files...");const s=await ve(r),n=await $e(r);o(`Local manifest has ${Object.keys(n).length} files`),a.text("Comparing with remote...");const i=await this.requestPlan(t.token,n,s?.commit);a.stop(),"legacy"===i?(o("v2 push endpoints unavailable, falling back to legacy zip push"),await this.pushLegacy(t.token,r,a)):await this.pushDiff(t.token,r,n,i,e,a),await this.promptAndDeploy(e)}catch(c){if(a.stop(),s(c),c instanceof Error&&c.message.startsWith("EXIT:"))throw c;if(n(c))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}async requestPlan(e,t,s){const n=`${u()}/business/v1/cli/v2/push/plan`;o(`POST ${n}`);const r=await fetch(n,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({manifest:t,lastSyncCommit:s})});if(o(`Plan response: ${r.status}`),r.ok)return await r.json();const a=await r.json().catch(()=>({}));if(((e,t)=>404===e&&"string"==typeof t.message&&t.message.startsWith("Cannot "))(r.status,a))return"legacy";this.failFromResponse(r.status,a,"Failed to push application.")}async pushDiff(e,t,s,n,r,a){let i=n;if(i.remoteChanged.length>0&&!r.force){console.error(m.red("Remote application has changes you don't have locally:"));for(const e of i.remoteChanged.slice(0,20))console.error(m.red(` ${e}`));i.remoteChanged.length>20&&console.error(m.red(` … and ${i.remoteChanged.length-20} more`)),console.error(m.red("Run 'dashnex app pull' first, or push anyway with --force.")),process.exit(1)}const c=i.upload.filter(e=>e in s);let l=i.delete;if(0!==c.length||0!==l.length){if(l.length>0){const e=await ve(t),s=r.yes||"1"===process.env.DASHNEX_CI;console.log(m.yellow(`${l.length} file(s) will be deleted from the application repository:`));for(const t of l.slice(0,20))console.log(m.yellow(` ${t}`));if(l.length>20&&console.log(m.yellow(` … and ${l.length-20} more`)),!s){const{confirmDelete:t}=await k.prompt([{type:"confirm",name:"confirmDelete",message:"Delete these files from the remote repository?",default:null!==e}]);t||(o("User declined remote deletions, pushing without them"),l=[])}}if(0!==c.length||0!==l.length)for(let n=1;;n+=1){a.start(1===n?"Uploading changes...":"Remote changed, retrying...");const d=await this.applyPush(e,t,c,l,i.baseCommit);if(a.stop(),"ok"===d.status){await ke(t,d.commit,s);const e=[`${c.length} file(s) updated`];return l.length>0&&e.push(`${l.length} deleted`),console.log(m.cyan(`Pushed: ${e.join(", ")}`)),void console.log(m.green("Application pushed successfully."))}n>=2&&(console.error(m.red("Remote repository keeps changing, please retry the push.")),process.exit(1)),o("Push apply conflicted, re-planning");const p=await ve(t),u=await this.requestPlan(e,s,p?.commit);"legacy"===u&&(console.error(m.red("Failed to push application.")),process.exit(1)),u.remoteChanged.length>0&&!r.force&&(console.error(m.red("Remote application changed during the push. Run 'dashnex app pull' first, or push with --force.")),process.exit(1)),i=u}else console.log(m.green("Everything up to date."))}else console.log(m.green("Everything up to date."))}async applyPush(e,t,s,n,r){const a=new E;for(const o of s){const e=y.join(t,...o.split("/")),s=y.posix.dirname(o);a.addLocalFile(e,"."===s?"":s,y.posix.basename(o))}const i=y.join(S.tmpdir(),`dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);try{a.writeZip(i),o(`Diff zip written to ${i} (${s.length} files, ${n.length} deletions)`),await this.ensureZipWithinLimit(i);const t=`${u()}/business/v1/cli/v2/push/apply`;o(`POST ${t}`);const c=await f.readFile(i),l=new FormData;l.append("file",new Blob([c],{type:"application/zip"}),"diff.zip"),l.append("baseCommit",r),l.append("deletions",JSON.stringify(n));const d=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${e}`},body:l});if(o(`Apply response: ${d.status}`),409===d.status)return{status:"conflict"};if(!d.ok){const e=await d.json().catch(()=>({}));this.failFromResponse(d.status,e,"Failed to push application.")}return{status:"ok",commit:(await d.json().catch(()=>({}))).commit??""}}finally{await f.remove(i).catch(()=>{})}}async pushLegacy(e,t,s){const n=await $e(t),r=Object.keys(n);o(`Legacy push including ${r.length} files`);const a=new E;for(const o of r){const e=y.join(t,...o.split("/")),s=y.posix.dirname(o);a.addLocalFile(e,"."===s?"":s,y.posix.basename(o))}const i=y.join(S.tmpdir(),`dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);try{s.start("Packaging application..."),a.writeZip(i),o(`Zip written to ${i}`),await this.ensureZipWithinLimit(i);const t=`${u()}/business/v1/cli/push`;o(`POST ${t}`);const n=await f.readFile(i),r=new FormData;r.append("file",new Blob([n],{type:"application/zip"}),"archive.zip"),s.text("Uploading...");const c=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${e}`},body:r});if(s.stop(),o(`Response: ${c.status}`),!c.ok){const e=await c.json().catch(()=>({}));this.failFromResponse(c.status,e,"Failed to push application.")}console.log(m.green("Application pushed successfully."))}finally{await f.remove(i).catch(()=>{}),o("Temp zip removed")}}async ensureZipWithinLimit(e){const{size:t}=await f.stat(e);if(t>Me){const e=e=>(e/1048576).toFixed(1);console.error(m.red(`Push package is ${e(t)} MB — the maximum is ${e(Me)} MB.`)),console.error(m.red("Remove or .gitignore large files and try again.")),process.exit(1)}}failFromResponse(e,t,o){let s=o;if(401===e||403===e)s="Please run 'dashnex login' to authenticate.";else{const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(t);e&&(s=e)}console.error(m.red(s)),process.exit(1)}async promptAndDeploy(e){let t;void 0!==e.deploy?(t=e.deploy,o(`Deploy decided via flag: ${t}`)):"1"===process.env.DASHNEX_CI?(t=!1,o("CI mode: skipping deploy prompt (use --deploy to deploy)")):(t=(await k.prompt([{type:"confirm",name:"deploy",message:"Deploy the application?",default:!0}])).deploy,o(`User chose to deploy: ${t}`)),t&&(o("Running deploy flow"),await(new Ue).execute())}},options:[{flags:"--deploy",description:"Auto-deploy after push (skip prompt)"},{flags:"--no-deploy",description:"Skip deploy prompt"},{flags:"--no-check",description:"Skip pre-push checks"},{flags:"-f, --force",description:"Push even when the remote has changes you don't have locally"},{flags:"-y, --yes",description:"Skip prompts (CI mode)"}]},{name:"delete",description:"Delete application from DashNex",handler:new class{async execute(e={}){if(o("Delete flow started"),!e.yes){const{confirm:e}=await k.prompt([{type:"confirm",name:"confirm",message:"Are you sure you want to delete this application? This action cannot be undone.",default:!1}]);if(!e)return o("User cancelled deletion"),void console.log(m.yellow("Deletion cancelled."))}const t=await h();if(!t)return;const r=`${u()}/business/v1/applications`;o(`DELETE ${r}`);const a=M();a.start("Deleting application...");try{const e=await fetch(r,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(a.stop(),o(`Response: ${e.status}`),!e.ok){const t=e.headers.get("content-type")??"";let o="Failed to delete application.";if(401===e.status||403===e.status)o="Please run 'dashnex login' to authenticate.";else if(t.includes("application/json")){const t=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(await e.json().catch(()=>({})));t&&(o=t)}console.error(m.red(o)),process.exit(1)}const s=await e.json().catch(()=>({})),n="string"==typeof s.message?s.message:"Application deleted successfully.";o("Application deleted successfully"),console.log(m.green(n))}catch(c){if(a.stop(),s(c),c instanceof Error&&c.message.startsWith("EXIT:"))throw c;if(n(c))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[{flags:"-y, --yes",description:"Skip confirmation prompt"}]},{name:"deploy",description:"Deploy application to DashNex",handler:new Ue,options:[{flags:"--no-check",description:"Skip pre-deploy checks"}]},{name:"status",description:"Show current application status",handler:new class{async execute(){o("Status flow started");const e=await h();if(!e)return;const t=`${u()}/business/v1/applications`;o(`GET ${t}`);const r=M();r.start("Fetching application status...");try{const s=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}});if(r.stop(),o(`Response: ${s.status}`),404===s.status)return void console.log(m.yellow("Application not created"));if(!s.ok){const e=s.headers.get("content-type")??"";let t="Failed to fetch application status.";if(401===s.status||403===s.status)t="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e=await s.json().catch(()=>({}));"string"==typeof e.error?t=e.error:"string"==typeof e.message&&(t=e.message)}console.error(m.red(t)),process.exit(1)}const n=await s.json().catch(()=>({}));o(`Application body: ${JSON.stringify(n)}`);const a=n.status??"unknown";console.log(`Status: ${m.cyan(a)}`);const i=Oe(n);i&&console.log(`URL: ${m.cyan(i)}`)}catch(a){if(r.stop(),s(a),a instanceof Error&&a.message.startsWith("EXIT:"))throw a;if(n(a))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[]},{name:"clear",description:"Delete local dependencies, caches and build output",handler:new class{async execute(e={}){o("Clear flow started");const t=process.cwd();f.pathExistsSync(y.join(t,"dashnex.json"))||(console.error(m.red("Run this command in a DashNex Web application folder.")),process.exit(1));const r=e.yes||"1"===process.env.DASHNEX_CI;o(`Non-interactive: ${r}`);try{const e=await this.collectTargets(t),n=y.join(t,...at.segments),i=await f.pathExists(n),c=i&&!r;if(i&&r&&e.push(at),0===e.length&&!c)return void console.log(m.yellow("Nothing to clear."));const l=[];if(e.length>0){console.log("The following will be deleted:");for(const t of e)console.log(` ${m.cyan(t.label)} ${m.dim(`(${t.description})`)}`);if(!r){const{confirm:e}=await k.prompt([{type:"confirm",name:"confirm",message:"Delete these files?",default:!1}]);if(!e)return o("User cancelled clear"),void console.log(m.yellow("Cancelled."))}for(const n of e){const e=y.join(t,...n.segments);o(`Removing ${e}`);try{await it(e),console.log(m.green(`Removed ${n.label}`))}catch(a){s(a),l.push(n.label)}}}if(c){const{confirm:e}=await k.prompt([{type:"confirm",name:"confirm",message:"Delete the local database as well?",default:!1}]);if(e){o(`Removing ${n}`);try{await it(n),console.log(m.green(`Removed ${at.label}`))}catch(a){s(a),l.push(at.label)}}}if(l.length>0){for(const e of l)console.error(m.red(`Could not delete ${e} — it may be in use. Stop 'dashnex dev' and try again.`));process.exit(1)}console.log(m.green("Clear complete. Run 'dashnex install' to set the application up again."))}catch(a){if(a instanceof Error&&a.message.startsWith("EXIT:"))throw a;if(s(a),n(a))return void console.log(m.yellow(i));throw a}}async collectTargets(e){const t=[];for(const o of rt)await f.pathExists(y.join(e,...o.segments))&&t.push(o);return t.filter(o=>!t.some(t=>t!==o&&((e,t)=>{const o=y.relative(t,e);return""!==o&&!o.startsWith("..")&&!y.isAbsolute(o)})(y.join(e,...o.segments),y.join(e,...t.segments))))}},options:[{flags:"-y, --yes",description:"Skip prompts and keep the local database (CI mode)"}]},{name:"tail",description:"Stream live console/debug logs from the deployed worker (Ctrl+C to stop)",handler:new class{async execute(e={}){const t=e.level??"log";Ve(t)||(console.error(m.red(`Invalid --level '${e.level}'. Use one of: log, info, warn, error, debug.`)),process.exit(1));let n=null;if(e.filter)try{n=new RegExp(e.filter)}catch{console.error(m.red(`Invalid --filter regular expression: ${e.filter}`)),process.exit(1)}const r=await h();if(!r)return;const a=!!e.debug;let i;try{i=await async function(e,t={}){const s=`${u()}${Xe}`;o(`POST ${s}${t.debug?" (debug)":""}`);const n=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({debug:!!t.debug})});if(o(`Response: ${n.status}`),401===n.status||403===n.status)throw new We("Not authorised to tail this application.");if(404===n.status)throw new Je("Business has no application, or the tail endpoint is unavailable.");if(!n.ok)throw new Ge(`Failed to start tail (status ${n.status}).`);const r=await n.json().catch(()=>({}));if(!r.token||!r.workerUrl)throw new Ge("Malformed tail-token response from the platform.");return{token:r.token,workerUrl:r.workerUrl,expiresAt:"number"==typeof r.expiresAt?r.expiresAt:0}}(r.token,{debug:a})}catch(k){s(k),k instanceof We&&(console.error(m.red(k.message)),process.exit(1)),k instanceof Je&&(console.error(m.red(k.message)),process.exit(2)),console.error(m.red(k instanceof Error?k.message:"Could not start tail. Check your connection.")),process.exit(3)}const c={color:!!process.stdout.isTTY},l={minLevel:t,filter:n},d=(p=i.workerUrl,`${/^(localhost|127\.0\.0\.1)/.test(p)?"ws":"wss"}://${p}/api/realtime/tail`);var p;const g=`dashnex.tail.token.${i.token}`;console.log(m.dim(`Tailing ${i.workerUrl} — Ctrl+C to stop.`)),a&&console.log(m.dim("Debug logging enabled on the worker (DEBUG=1); may take a minute to take effect."));let f=!1,y=Date.now();const w=setInterval(()=>{f||Date.now()-y>=3e4&&(console.log(m.dim("(no logs in 30s)")),y=Date.now())},5e3),$=new T(d,g,{WebSocket:P,connectionTimeout:1e4,maxReconnectionDelay:16e3}),v=async e=>{if(!f){f=!0,clearInterval(w);try{$.close()}catch{}await Promise.race([qe(r.token,{debug:a}),new Promise(e=>setTimeout(e,3e3))]),process.exit(e)}};process.on("SIGINT",()=>{console.log(),v(0)}),$.binaryType="arraybuffer",$.addEventListener("message",e=>{y=Date.now();const t=st("string"==typeof e.data?e.data:"",l,c);"line"===t.kind?console.log(t.line):"malformed"===t.kind&&console.error(m.dim(`[malformed frame] ${t.raw}`))}),$.addEventListener("close",e=>{if(!f)return nt.has(e.code)?(console.error(m.red("Tail token was rejected (auth). Exiting.")),void v(1)):void o(`socket closed (code ${e.code}); reconnecting…`)}),$.addEventListener("error",e=>{o(`socket error: ${e.message??"unknown"}`)}),await new Promise(()=>{})}},options:[{flags:"--level <level>",description:"Minimum level: log, info, warn, error, debug"},{flags:"--filter <regex>",description:"Only show records whose args match this regex"},{flags:"--debug",description:"Also enable DEBUG=1 on the worker so debug-gated logs are shown"}]}]},lt={name:"secrets",description:"Manage DashNex secrets",handler:{execute:async()=>{}},options:[],subcommands:[{name:"list",description:"List all secret names bound to the worker",handler:new class{async execute(){o("Secrets list flow started");const e=await h();if(!e)return;const t=`${u()}/business/v1/cli/secrets`;o(`GET ${t}`);const r=M();r.start("Fetching secrets...");try{const s=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}});if(r.stop(),o(`Response: ${s.status}`),!s.ok){let e="Failed to fetch secrets.";401===s.status||403===s.status?e="Please run 'dashnex login' to authenticate.":404===s.status?e="Business has no application.":502===s.status&&(e="Error fetching secrets. Make sure your application is deployed."),console.error(m.red(e)),process.exit(1)}const n=(await s.json()).secrets??[];if(0===n.length)return void console.log(m.yellow("No secrets set."));console.log(m.bold(`\nSecrets (${n.length}):\n`)),n.forEach(({name:e})=>{console.log(` ${m.cyan(e)}`)}),console.log("")}catch(a){if(r.stop(),s(a),a instanceof Error&&a.message.startsWith("EXIT:"))throw a;if(n(a))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[]},{name:"set",description:"Create or update a secret (prompts for missing KEY/VALUE)",handler:new class{async execute(e={}){o("Secrets set flow started");let{name:t,value:r}=e;const a="1"===process.env.DASHNEX_CI;t||(a&&(console.error(m.red("--name is required in CI mode (--ci).")),process.exit(1)),t=(await k.prompt([{type:"input",name:"name",message:"Secret name:",validate:e=>""!==e.trim()||"Name is required"}])).name.trim()),r||(a&&(console.error(m.red("--value is required in CI mode (--ci).")),process.exit(1)),r=(await k.prompt([{type:"password",name:"value",message:`Enter value for ${t}:`,mask:"*",validate:e=>""!==e||"Value is required"}])).value);const c=await h();if(!c)return;const l=`${u()}/business/v1/cli/secrets`;o(`PUT ${l}`);const d=M();d.start(`Setting secret '${t}'...`);try{const e=await fetch(l,{method:"PUT",headers:{Authorization:`Bearer ${c.token}`,"Content-Type":"application/json"},body:JSON.stringify({name:t,value:r})});if(d.stop(),o(`Response: ${e.status}`),!e.ok){let t="Failed to set secret.";401===e.status||403===e.status?t="Please run 'dashnex login' to authenticate.":400===e.status?t="Missing or empty name / value.":404===e.status?t="Business has no application.":502===e.status&&(t="Error setting secret. Make sure your application is deployed."),console.error(m.red(t)),process.exit(1)}o(`Secret '${t}' set successfully`),console.log(m.green(`Secret '${t}' set successfully.`))}catch(p){if(d.stop(),s(p),p instanceof Error&&p.message.startsWith("EXIT:"))throw p;if(n(p))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},arguments:[{name:"name",description:"Secret name (KEY)"},{name:"value",description:"Secret value"}],options:[{flags:"--name <name>",description:"Secret name (alternative to positional KEY)"},{flags:"--value <value>",description:"Secret value (alternative to positional VALUE)"}]},{name:"delete",description:"Delete a secret (prompts for missing KEY)",handler:new class{async execute(e={}){o("Secrets delete flow started");let{name:t}=e;const r="1"===process.env.DASHNEX_CI;t||(r&&(console.error(m.red("--name is required in CI mode (--ci).")),process.exit(1)),t=(await k.prompt([{type:"input",name:"name",message:"Secret name:",validate:e=>""!==e.trim()||"Name is required"}])).name.trim());const a=await h();if(!a)return;const c=`${u()}/business/v1/cli/secrets/${encodeURIComponent(t)}`;o(`DELETE ${c}`);const l=M();l.start(`Deleting secret '${t}'...`);try{const e=await fetch(c,{method:"DELETE",headers:{Authorization:`Bearer ${a.token}`}});if(l.stop(),o(`Response: ${e.status}`),!e.ok){let o="Failed to delete secret.";401===e.status||403===e.status?o="Please run 'dashnex login' to authenticate.":404===e.status?o=`Secret '${t}' not found.`:502===e.status&&(o="Error deleting secret. Make sure your application is deployed."),console.error(m.red(o)),process.exit(1)}o(`Secret '${t}' deleted successfully`),console.log(m.green(`Secret '${t}' deleted.`))}catch(d){if(l.stop(),s(d),d instanceof Error&&d.message.startsWith("EXIT:"))throw d;if(n(d))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},arguments:[{name:"name",description:"Secret name (KEY)"}],options:[{flags:"--name <name>",description:"Secret name (alternative to positional KEY)"}]}]},dt=[{name:"version",description:"Display the version of the CLI",handler:new class{async execute(e){const t=y.dirname(w(import.meta.url)),o=JSON.parse(v.readFileSync(y.resolve(t,"..","..","package.json"),"utf8"));console.log(o.version)}},options:[]},{name:"login",description:"Log in to your DashNex account",handler:new W,options:[{flags:"--business <id>",description:"Business ID to select (skip prompt)"},{flags:"--no-browser",description:"Print the sign-in URL instead of opening a browser"}]},{name:"logout",description:"Log out and remove local credentials",handler:new class{async execute(){const e=r(process.cwd());if(await f.pathExists(e)){const t=await f.readJson(e);delete t.token,delete t.refreshToken,delete t.businessId,await f.writeJson(e,t,{spaces:2})}console.log(m.green("Logged out."))}},options:[]},{name:"whoami",description:"Show current logged-in business and user",handler:new class{async execute(){o("Whoami flow started");const e=await h();if(!e)return;const t=await c()?`${u()}/business/v1/`:`${l()}/users/v1/`;o(`GET ${t}`);const r=M();r.start("Fetching user info...");try{const s=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}});if(r.stop(),o(`Response: ${s.status}`),!s.ok){const e=s.headers.get("content-type")??"";let t="Failed to fetch user info.";if(401===s.status||403===s.status)t="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e="string"==typeof(a=await s.json().catch(()=>({}))).error?a.error:"string"==typeof a.message?a.message:void 0;e&&(t=e)}console.error(m.red(t)),process.exit(1)}const n=await s.json().catch(()=>({})),i="string"==typeof n.name?n.name:"string"==typeof n.companyName?n.companyName:"Unknown",c=n.memberUser,l=c&&"string"==typeof c.fullName?c.fullName:c&&"string"==typeof c.name?c.name:"Unknown";o(`Whoami: ${i} (${l})`),console.log(m.green(`You are logged in as ${i} (${l})`))}catch(d){if(r.stop(),s(d),d instanceof Error&&d.message.startsWith("EXIT:"))throw d;if(n(d))return void console.log(m.yellow(i));console.error(m.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}var a}},options:[]},{name:"dev",description:"Run the development server (run in DashNex application folder)",handler:new class{async execute(e={}){o("Dev flow started");try{const t=await q(process.cwd(),e);process.exit(t)}catch(t){if(t instanceof Error&&t.message.startsWith("EXIT:"))throw t;s(t),t instanceof Error?console.error(m.red(t.message)):console.error(m.red("Failed to run npm run dev")),process.exit(1)}}},options:[{flags:"--https",description:"Run dev server with HTTPS"}]},{name:"check",description:"Run build-time validations (run in DashNex application folder)",handler:new ae,options:[]},{name:"install",description:"Install dependencies using pnpm (preferred) or npm (run in DashNex application folder)",handler:new class{async execute(){o("Install flow started");const e=y.join(process.cwd(),"dashnex.json");await f.pathExists(e)||(console.error(m.red("Run this command in a DashNex Web application folder.")),process.exit(1));const t=process.cwd(),n=M();n.start("Installing dependencies...");try{const e=await H(t);n.stop(),0!==e&&process.exit(e),console.log(m.green("Dependencies installed.")),process.exit(0)}catch(r){n.stop(),s(r),r instanceof Error?console.error(m.red(r.message)):console.error(m.red("Failed to run install")),process.exit(1)}}},options:[]},ct,lt];export{dt as default};
|
|
1
|
+
import{a as e,c as t,d as o,f as s,h as n,i as r,l as a,m as i,n as c,o as l,p as d,r as p,s as u,t as h,u as m}from"../chunks/auth-ezwXxi-m.js";import f from"chalk";import g from"fs-extra";import w from"path";import{fileURLToPath as y}from"url";import{execFileSync as $,spawn as v,spawnSync as k}from"child_process";import x from"fs";import b from"inquirer";import{X509Certificate as S,createHash as E,createPrivateKey as D,randomBytes as T}from"crypto";import C from"net";import P from"os";import A from"adm-zip";import{diffLines as j}from"diff";import N from"ignore";import{WebSocket as I}from"partysocket";import F from"ws";import{inspect as O}from"node:util";import{rm as R}from"node:fs/promises";var B=e=>e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,""),_=async e=>{const t=(e=>{switch(process.platform){case"darwin":return{command:"open",args:[e]};case"win32":return{command:"cmd",args:["/c","start","",e]};default:return{command:"xdg-open",args:[e]}}})(e);return!!t&&new Promise(e=>{try{const n=v(t.command,t.args,{stdio:"ignore",detached:!0});n.on("error",t=>{s(t),e(!1)}),n.on("spawn",()=>{o(`Opened browser via ${t.command}`),n.unref(),e(!0)})}catch(n){s(n),e(!1)}})},z="urn:ietf:wg:oauth:2.0:oob",L=class extends Error{constructor(e,t){super(e),this.code=t,this.name="OAuthError"}},U=class extends Error{},M=class extends U{},W=e=>{const t=e.data;return t&&"object"==typeof t?t:e},J=e=>{const t=e.items??[];return Array.isArray(t)?t.map(e=>{if(!e||"object"!=typeof e)return null;const t=e,o=Number(t.id);return!Number.isFinite(o)||o<=0?null:{id:o,name:String(t.name??t.companyName??`Business ${o}`).trim()}}).filter(e=>null!==e):[]},H=async(s,n)=>{const r=[];let a=1,i=0;do{const c=`${t()}${s}?page=${a}&perPage=100`;o(`GET ${c}`);const{response:l,body:p}=await e(c,{headers:{Authorization:`Bearer ${n}`}});if(o(`Response: ${l.status}`),d(`Businesses response (${s})`,p),!l.ok){if(401===l.status)throw new M("Your DashNex ID session has expired.");if(404===l.status)throw new U(`Your DashNex ID account is not linked to a contact yet. Finish signing up at ${t()} and try again.`);throw new U("Failed to fetch businesses. Please try again.")}const u=W(p),h=J(u);if(i="number"==typeof u.total?u.total:h.length,r.push(...h),h.length<100||r.length>=i)break;a++}while(r.length<i);return r},G=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],X=(e="")=>{if(process.env.DASHNEX_CI)return{start:()=>{},stop:()=>{},text:()=>{}};let t=null,o=0,s=e;const n=()=>{const e=G[o%G.length];process.stderr.write(`\r[K${e} ${s}`),o++};return{start:e=>{void 0!==e&&(s=e),t||(o=0,n(),t=setInterval(n,80))},stop:()=>{t&&(clearInterval(t),t=null),process.stderr.write("\r[K\n")},text:e=>{s=e,t&&n()}}},q=class a{async execute(e={}){o("Login flow started");const t=r(process.cwd()),a=await h({exitOnFailure:!1,dashnexPath:t});if(a)console.log(f.green(`Already logged in as ${a.userName??"user"}`));else{m()||(console.error(f.red("No DashNex ID OAuth client is configured for this environment.")),console.error(f.yellow("Set DASHNEX_OAUTH_CLIENT_ID, or use APPLICATION_ENV=qa.")),process.exit(1));try{const s=await this.readSavedIdToken(t);if(s){o("Resuming with saved DashNex ID session");try{return void(await this.selectBusiness(s,t,e.business))}catch(c){if(!(c instanceof M))throw c;o("Saved DashNex ID session rejected, starting a new sign-in")}}const n=await this.authorize(!1!==e.browser);await this.writeConfig(t,{idToken:n.token,...n.refreshToken&&{idRefreshToken:n.refreshToken}}),await this.selectBusiness(n.token,t,e.business)}catch(c){if(s(c),n(c))return void console.log(f.yellow(i));if((c instanceof L||c instanceof U)&&(console.error(f.red(c.message)),process.exit(1)),c instanceof Error&&c.message.startsWith("EXIT:"))throw c;console.error(f.red("Could not reach DashNex. Check your connection and try again.")),process.exit(1)}}}async authorize(s){const{verifier:n,challenge:r}=(()=>{const e=B(T(32));return{verifier:e,challenge:B(E("sha256").update(e).digest())}})(),a=(e=>{const o=new URL(`${t()}/api/oauth/authorize`);return o.searchParams.set("client_id",m()),o.searchParams.set("redirect_uri",z),o.searchParams.set("response_type","code"),o.searchParams.set("code_challenge",e),o.searchParams.set("code_challenge_method","S256"),o.toString()})(r);o(`Authorize URL: ${a}`);const i=!!s&&await _(a);console.log(""),i?(console.log(f.green("Opened your browser to finish signing in.")),console.log(f.dim("If nothing opened, use this URL:"))):console.log(f.bold("Open this URL to sign in:")),console.log(f.cyan(a)),console.log(""),console.log(f.dim("Approve the request, then copy the code shown and paste it below.")),console.log("");const{code:c}=await b.prompt([{type:"input",name:"code",message:"Paste the code:",validate:e=>!!e.trim()||"Code is required"}]),l=X();l.start("Verifying code...");try{return await(async(s,n)=>{const r=`${t()}/api/oauth/token`;o(`POST ${r}`);const{response:a,body:i}=await e(r,{method:"POST",body:JSON.stringify({grant_type:"authorization_code",client_id:m(),redirect_uri:z,code:s.trim(),code_verifier:n})});if(o(`Response: ${a.status}`),d("Token response",i),!a.ok){const e="string"==typeof i.error?i.error:void 0,t="string"==typeof i.error_description?i.error_description:void 0;if("invalid_grant"===e)throw new L("That code is not valid anymore. Codes expire 10 minutes after they are issued and can only be used once.",e);throw new L(t??"Could not exchange the code for a token.",e)}const c=i;if(!c.access_token)throw new L("The authorization server did not return an access token.");return{token:c.access_token,refreshToken:c.refresh_token,expiresIn:c.expires_in}})(c,n)}finally{l.stop()}}async selectBusiness(s,n,r){const a=X();let i;a.start("Fetching businesses...");try{i=await(async e=>{const t=await H("/api/businesses/my",e),s=await H("/api/businesses/other",e),n=/* @__PURE__ */new Map;for(const o of t)n.set(o.id,o);for(const o of s)n.has(o.id)||n.set(o.id,o);return o(`Businesses (my: ${t.length}, other: ${s.length}, merged: ${n.size})`),[...n.values()]})(s)}finally{a.stop()}0===i.length&&(console.error(f.red("No businesses or teams found for your account!")),console.log(f.green(`Create your first business at ${t()}`)),process.exit(1));const c=await this.pickBusiness(i,r);let l;a.start("Switching business...");try{l=await(async(s,n)=>{const r=`${t()}/api/businesses/${n}/switch`;o(`POST ${r}`);const{response:a,body:i}=await e(r,{method:"POST",headers:{Authorization:`Bearer ${s}`}});if(o(`Response: ${a.status}`),d("Switch response",i),!a.ok){if(401===a.status)throw new M("Your DashNex ID session has expired.");if(403===a.status)throw new U("You do not have access to that business.");throw new U("Failed to switch to business. Please try again.")}const c=W(i);if(!c.token||!c.refreshToken)throw new U("Invalid response from server. Please try again.");return{token:c.token,refreshToken:c.refreshToken,businessId:n}})(s,c.id)}finally{a.stop()}const p={token:l.token,refreshToken:l.refreshToken,businessId:l.businessId,...await this.fetchCliSettings(l.token)};await this.writeConfig(n,p),await this.ensureGitignore(),console.log(f.green(`Logged in as ${c.name}`))}async pickBusiness(e,t){if(t){const s=e.find(e=>String(e.id)===String(t).trim());return s||(console.error(f.red(`Business "${t}" not found.`)),process.exit(1)),o(`Selected business via flag: ${s.id} ${s.name}`),s}if(1===e.length)return o(`Selected business: ${e[0].id} ${e[0].name}`),e[0];const{businessId:s}=await b.prompt([{type:"select",name:"businessId",message:"Select business:",choices:e.map(e=>({name:e.name,value:e.id}))}]),n=e.find(e=>e.id===s)||e[0];return o(`Selected business: ${n.id} ${n.name}`),n}async readSavedIdToken(e){if(!(await g.pathExists(e)))return null;try{return(await g.readJson(e)).idToken??null}catch{return null}}async fetchCliSettings(t){try{const s=`${u()}/business/v1/cli/settings`;o(`GET ${s}`);const{response:n,body:r}=await e(s,{headers:{Authorization:`Bearer ${t}`}});if(o(`Response: ${n.status}`),d("CLI settings response",r),n.ok&&r&&"object"==typeof r)return r}catch(n){s(n)}return{}}static{this.OBSOLETE_KEYS=["issuer"]}async writeConfig(e,t){const s=w.dirname(e);await g.pathExists(s)&&(await g.stat(s)).isFile()&&await g.remove(s),await g.ensureDir(s);let n=t;if(await g.pathExists(e))try{n={...await g.readJson(e),...t};for(const e of a.OBSOLETE_KEYS)delete n[e]}catch{o("Existing settings.json could not be parsed, overwriting")}await g.writeJson(e,n,{spaces:2}),o("Saved credentials to .dashnex/settings.json")}async ensureGitignore(){const e=w.join(process.cwd(),".gitignore");if(!(await g.pathExists(e)))return;let t=await g.readFile(e,"utf8");t.includes(".dashnex")||(t=t.trimEnd(),t.endsWith("\n")||(t+="\n"),t+="\n.dashnex\n",await g.writeFile(e,t))}},Y="v1.4.4",V=w.join(p,"certificates"),K=["localhost","127.0.0.1","::1"],Z="win32"===process.platform,Q=()=>0===k("mkcert",["-version"],{stdio:"ignore",shell:Z}).status?"mkcert":null,ee=async(e={})=>{const t=(e.lookupPath??Q)();if(t)return t;const o=e.cacheDir??w.join(P.homedir(),".dashnex","mkcert"),s=((e=process.platform,t=process.arch)=>{const o="x64"===t?"amd64":t;if("win32"===e)return`mkcert-${Y}-windows-${o}.exe`;if("darwin"===e)return`mkcert-${Y}-darwin-${o}`;if("linux"===e)return`mkcert-${Y}-linux-${o}`;throw new Error(`Unsupported platform for mkcert: ${e}`)})(),n=w.join(o,s);if(await g.pathExists(n))return n;const r=`https://github.com/FiloSottile/mkcert/releases/download/${Y}/${s}`;e.log?.(`mkcert not found, downloading ${Y} to ${n}`);const a=await(e.fetchImpl??fetch)(r);if(!a.ok)throw new Error(`Failed to download mkcert from ${r}: HTTP ${a.status}`);await g.ensureDir(o);const i=`${n}.download`;return await g.writeFile(i,Buffer.from(await a.arrayBuffer())),await g.chmod(i,493),await g.move(i,n,{overwrite:!0}),n},te=(e,t,o)=>{try{const s=new S(g.readFileSync(t)),n=D(g.readFileSync(e));return!!s.checkPrivateKey(n)&&(!(new Date(s.validTo).getTime()<=Date.now())&&o.every(e=>((e,t)=>C.isIP(t)?Boolean(e.checkIP(t)):Boolean(e.checkHost(t)))(s,e)))}catch{return!1}},oe=async(e,t={})=>{const o=t.log??(()=>{}),n=t.certDir??V,r=w.resolve(e,n),a=w.join(r,"localhost-key.pem"),i=w.join(r,"localhost.pem"),c=(e=>{const t=(e??"").trim();return t&&!K.includes(t)?[...K,t]:[...K]})(t.host);try{if(te(a,i,c))return o(`Using existing local certificate from ${w.relative(e,r)||"."}`),{key:a,cert:i};const s=await(t.resolveMkcert??(()=>ee({log:o})))();if(await g.ensureDir(r),o("Generating a trusted local certificate with mkcert (this may prompt for your password once)..."),$(s,["-install","-key-file",a,"-cert-file",i,...c],{stdio:"inherit",shell:Z}),!(await g.pathExists(a))||!(await g.pathExists(i)))throw new Error("mkcert finished but the certificate files were not created");let l;await(async(e,t)=>{const o=w.join(e,".gitignore");if(!(await g.pathExists(o)))return!1;const s=await g.readFile(o,"utf8"),n=s.split(/\r?\n/).map(e=>e.trim()),r=new Set([t,`/${t}`,`${t}/`,`/${t}/`]);if(n.some(e=>r.has(e)))return!1;const a=0===s.length||s.endsWith("\n")?"":"\n";return await g.appendFile(o,`${a}${t}\n`),!0})(e,n.split(w.sep)[0]);try{const e=$(s,["-CAROOT"],{encoding:"utf8",shell:Z}).trim();e&&(l=w.join(e,"rootCA.pem"))}catch{}return o(`Certificates created in ${w.relative(e,r)||"."}`),{key:a,cert:i,...l?{rootCA:l}:{}}}catch(l){return s(l),o(`Failed to set up HTTPS certificates: ${l instanceof Error?l.message:String(l)}`),null}},se=["Using (vars|secrets) defined in ","\\(node:\\d+\\) MaxListenersExceededWarning:","\\(Use `node --trace-warnings","\\[DEP0205\\] DeprecationWarning:","\\(Use `node --trace-deprecation","Detected multiple renderers concurrently rendering the same context provider","\\[WARNING\\].*You have defined bindings to the following internal Durable Objects","You have defined bindings to the following internal Durable Objects",'\\{"name":".+","class_name":".+"\\}',"These will not work in local development, but they should work in production","If you want to develop these locally, you can define your DO in a separate Worker","For detailed instructions, refer to the Durable Objects section here:","developers.cloudflare.com/workers/wrangler/api#supported-bindings","workerd/server/server\\.c\\+\\+.*warning:.*DurableObjectNamespace"],ne=(e,t)=>t.length>0&&t.some(t=>t.test(e)),re=(e,t)=>{let s="";return{write:n=>{const r="string"==typeof n?n:n.toString();s+=r;const a=s.split(/\r?\n/);s=a.pop()??"";for(const s of a){const n=s.trim();n?ne(n,t)?o(`Filtered: ${n}`):e.write(s+"\n"):o("Filtered: <empty line>")}},flush:()=>{const n=s.trim();if(n){if(ne(n,t))return o(`Filtered: ${n}`),void(s="");e.write(s),s=""}else s=""}}},ae=async(e,t={})=>{const n=w.join(e,"dashnex.json"),r=w.join(e,"package.json");if(o(`Checking for dashnex.json at: ${n}`),!(await g.pathExists(n)))throw new Error("Run this command in a DashNex Web application folder.");if(o("dashnex.json found, checking for package.json"),!(await g.pathExists(r)))throw new Error("package.json is missing.");let a;o("package.json found, checking for dev script");try{a=await g.readJson(r)}catch(h){throw s(h),new Error("Failed to read package.json.")}if(!a.scripts||!a.scripts.dev)throw new Error('The "dev" script is missing from package.json. Please add a "dev" script to package.json.');const i=((e,t=process.env)=>{const o=e.port??t.PORT;if(null==o||""===String(o).trim())return;const s=Number(o);if(!Number.isInteger(s)||s<1||s>65535)throw new Error(`Invalid port: ${o}. Use a number between 1 and 65535.`);return s})(t),c=((e,t=process.env)=>{const o=(e.host??t.HOST??"").trim();return""===o?void 0:o})(t);o("dev script found, checking for npm availability");const l=await(async e=>new Promise(t=>{const o=v(`${e} --version`,{stdio:"ignore",shell:!0});o.on("error",()=>{t(!1)}),o.on("close",e=>{t(0===e)})}))("npm");if(o(`npm available: ${l}`),!l)throw new Error("npm is not available. Please install npm to continue.");const d={};if(t.https){const t=await oe(e,{host:c,log:e=>console.log(f.dim(e))});t?(d.DASHNEX_DEV_HTTPS_KEY=t.key,d.DASHNEX_DEV_HTTPS_CERT=t.cert,console.log(f.green("HTTPS enabled for the dev server"+(i?` on https://${c??"localhost"}:${i}`:""))),t.rootCA&&console.log(f.dim(`Local CA: ${t.rootCA} (import it in browsers on other machines, e.g. when the app runs in Docker)`))):console.log(f.yellow("HTTPS is not available, starting the dev server over HTTP instead."))}const p=[];void 0!==i&&p.push("--port",String(i)),void 0!==c&&p.push("--hostname",c);const u=p.length?["--",...p]:[];return o(`npm available, executing: npm run dev${u.length?` ${u.join(" ")}`:""} in ${e}`),(async(e,t=[],o=process.cwd(),n=[],r={})=>{const a=t.length?`${e} ${t.join(" ")}`:e,i=n.length>0;return new Promise((e,t)=>{const c=v(a,{stdio:i?["inherit","pipe","pipe"]:"inherit",cwd:o,shell:!0,env:{...process.env,NODE_ENV:"development",FORCE_COLOR:"1",...r}});if(c.on("error",e=>{s(e),t(e)}),i&&c.stdout&&c.stderr){const t=re(process.stdout,n),o=re(process.stderr,n);c.stdout.on("data",e=>t.write(e)),c.stderr.on("data",e=>o.write(e)),c.on("close",s=>{t.flush(),o.flush(),e(null===s?0:s)})}else c.on("close",t=>{e(null===t?0:t)})})})("npm run dev",u,e,(()=>{const e=[];for(const t of se)try{e.push(new RegExp(t))}catch{o(`Invalid dev output filter pattern ignored: ${t}`)}return e})(),d)},ie=async e=>new Promise(t=>{const o=v(`${e} --version`,{stdio:"ignore",shell:!0});o.on("error",()=>{t(!1)}),o.on("close",e=>{t(0===e)})}),ce=async e=>{let t;if(await ie("pnpm"))t="pnpm install --config.strict-dep-builds=false",o("Using package manager: pnpm");else{if(!(await ie("npm")))throw new Error("Neither pnpm nor npm is available. Install Node.js (which bundles npm) or pnpm to continue.");t="npm install --legacy-peer-deps",o("Using package manager: npm (with --legacy-peer-deps for DashNex peer-dep overlap)")}return o(`Executing: ${t} in ${e}`),new Promise((o,n)=>{const r=[],a=[],i=v(t,{stdio:["ignore","pipe","pipe"],cwd:e,shell:!0,env:{...process.env,NPM_CONFIG_LOGLEVEL:"error"}});i.stdout&&i.stdout.on("data",e=>r.push(e)),i.stderr&&i.stderr.on("data",e=>a.push(e)),i.on("error",e=>{s(e),n(e)}),i.on("close",e=>{const t=null===e?0:e;0!==t&&(r.length&&process.stdout.write(Buffer.concat(r)),a.length&&process.stderr.write(Buffer.concat(a))),o(t)})})},le="Run this command in a DashNex Web application folder.",de=e=>{const t=w.join(e,"dashnex.json");return g.pathExistsSync(t)},pe=(e,t=!1)=>{t||console.log(f.cyan(e))},ue=(e,t,n={})=>{const r=`${e} ${t.join(" ")}`,a=!0===n.silent;return new Promise((t,n)=>{o(`Running: ${r}`);const i=v(r,{stdio:a?["ignore","pipe","pipe"]:"inherit",cwd:process.cwd(),shell:!0});i.on("error",e=>{s(e),n(e)}),i.on("close",s=>{o(`${e} exited with code ${s}`),t(null===s?1:s)})})},he=(e,t)=>new Promise((n,r)=>{o(`Running: ${e} ${t.join(" ")}`);const a=v(e,t,{cwd:process.cwd()});let i="";a.stdout?.on("data",e=>{i+=e.toString()}),a.stderr?.on("data",e=>{i+=e.toString()}),a.on("error",e=>{s(e),r(e)}),a.on("close",t=>{o(`${e} exited with code ${t}`),n({exitCode:null===t?1:t,output:i})})}),me=async e=>{o("Running check: install");try{return 0===await ce(process.cwd())}catch(t){return s(t),!e&&t instanceof Error&&console.error(f.red(t.message)),!1}},fe=async()=>{try{const{output:e}=await he(process.execPath,[process.argv[1],"db","migrate","--help"]);return e.includes("Usage: dashnex db migrate")}catch(e){return s(e),!1}},ge=async e=>{o("Running check: db migrate");try{const{exitCode:t,output:s}=await he(process.execPath,[process.argv[1],"db","migrate"]);return 0===t||(s.includes("Unknown command")?(o("db migrate command is not available, skipping"),!0):(!e&&s.trim().length>0&&process.stderr.write(s),!1))}catch(t){return s(t),!0}},we=e=>new Promise(t=>{const o=v(`${e} --version`,{stdio:"ignore",shell:!0});o.on("error",()=>t(!1)),o.on("close",e=>t(0===e))}),ye=[{name:"TypeScript",command:"npx",args:["tsc","--noEmit"],notFoundMessage:"TypeScript is not available. Please install it with: npm install -D typescript"}],$e=class{async execute(){o("Check flow started"),de(process.cwd())||(console.error(f.red(le)),process.exit(1)),pe("Checking dependencies..."),await me(!1)||(console.error(f.red("Check failed.")),process.exit(1)),o(`Running ${ye.length} check(s)`);for(const t of ye){o(`Running check: ${t.name}`),pe(`Checking ${t.name}...`),await we(t.command)||(console.error(f.red(t.notFoundMessage)),process.exit(1));try{0!==await ue(t.command,t.args)&&(console.error(f.red("Check failed.")),process.exit(1))}catch(e){s(e),console.error(f.red(t.notFoundMessage)),process.exit(1)}}await fe()?(pe("Checking database migrations..."),await ge(!1)||(console.error(f.red("Check failed.")),process.exit(1))):o("db migrate command is not available, skipping"),console.log(f.green("All checks passed."))}async run(e=!1){if(o("Running checks (called from push)"),!de(process.cwd()))return e||console.error(f.red(le)),!1;if(pe("Checking dependencies...",e),!(await me(e)))return!1;const t=e?{silent:!0}:void 0;for(const r of ye){if(o(`Running check: ${r.name}`),pe(`Checking ${r.name}...`,e),!(await we(r.command)))return console.error(f.red(r.notFoundMessage)),!1;try{if(0!==await ue(r.command,r.args,t))return!1}catch(n){return s(n),console.error(f.red(r.notFoundMessage)),!1}}if(await fe()){if(pe("Checking database migrations...",e),!(await ge(e)))return!1}else o("db migrate command is not available, skipping");return e||console.log(f.green("All checks passed.")),!0}},ve=" │ ",ke=e=>e.subarray(0,8192).includes(0),xe=e=>{const t=e.split("\n");return""===t[t.length-1]&&t.pop(),t},be=(e,t)=>{if(null===e)return" ".repeat(t);const o=e.replace(/\t/g," ");return o.length<=t?o.padEnd(t):`${o.slice(0,Math.max(0,t-1))}…`},Se=(e,t,o)=>{const s=Math.max(o??process.stdout.columns??120,43),n=Math.floor((s-3)/2),r=((e,t)=>{const o=[],s=j(e,t);for(let n=0;n<s.length;n+=1){const e=s[n];if(e.removed){const t=xe(e.value),r=s[n+1],a=r?.added?xe(r.value):[];r?.added&&(n+=1);const i=Math.max(t.length,a.length);for(let e=0;e<i;e+=1)o.push({left:t[e]??null,right:a[e]??null,changed:!0})}else if(e.added)for(const t of xe(e.value))o.push({left:null,right:t,changed:!0});else for(const t of xe(e.value))o.push({left:t,right:t,changed:!1})}return o})(e,t),a=/* @__PURE__ */new Set;r.forEach((e,t)=>{if(e.changed)for(let o=Math.max(0,t-3);o<=Math.min(r.length-1,t+3);o+=1)a.add(o)});const i=[];i.push(f.bold(be("local (current)",n))+f.dim(ve)+f.bold("incoming"));let c=-1,l=0,d=0;for(let p=0;p<r.length;p+=1){if(!a.has(p))continue;if(l>=200){d+=1;continue}-1!==c&&p>c+1&&i.push(f.dim("···")),c=p,l+=1;const e=r[p],t=be(e.left,n),o=be(e.right,n);if(!e.changed){i.push(f.dim(t)+f.dim(ve)+f.dim(o));continue}const s=null!==e.left?f.red(t):t,u=null!==e.right?f.green(o):o;i.push(s+f.dim(ve)+u)}return d>0&&i.push(f.dim(`… ${d} more diff line(s) not shown`)),i.join("\n")},Ee=[".dashnex",".git",".vinext",".wrangler","wrangler.jsonc","wrangler.toml",".dev.vars",".github"],De=[".env","wrangler.jsonc"],Te=e=>w.join(e,p,"sync.json"),Ce=e=>E("sha1").update(`blob ${e.length}\0`).update(e).digest("hex"),Pe=e=>e.split(w.sep).join("/"),Ae=e=>Ee.some(t=>e===t||e.startsWith(`${t}/`)),je=async e=>{const t=await(async e=>{const t=N();t.add(Ee);const o=w.join(e,".gitignore");return await g.pathExists(o)&&t.add(await g.readFile(o,"utf8")),t})(e),o=[],s=async n=>{const r=await g.readdir(n,{withFileTypes:!0});for(const a of r){const r=w.join(n,a.name),i=Pe(w.relative(e,r));Ae(i)||t.ignores(a.isDirectory()?`${i}/`:i)||(a.isDirectory()?await s(r):a.isFile()&&o.push(i))}};return await s(e),o},Ne=async(e,t={})=>{const o={},s=await je(e);for(const n of s)o[n]=Ce(await g.readFile(w.join(e,n)));if(t.includeGenerated)for(const n of De){if(n in o)continue;const t=w.join(e,n);(await g.lstat(t).catch(()=>null))?.isFile()&&(o[n]=Ce(await g.readFile(t)))}return o},Ie=async e=>{try{const t=await g.readFile(Te(e),"utf8"),o=JSON.parse(t);return"string"!=typeof o?.commit||"object"!=typeof o?.manifest||null===o.manifest?null:o}catch{return null}},Fe=async(e,t,s)=>{const n={commit:t,manifest:s,updatedAt:/* @__PURE__ */(new Date).toISOString()},r=Te(e);try{await g.ensureDir(w.dirname(r)),await g.writeFile(r,`${JSON.stringify(n,null,2)}\n`),o(`Sync state written: commit ${t}, ${Object.keys(s).length} files`)}catch(a){o(`Failed to write sync state: ${a instanceof Error?a.message:"unknown"}`)}},Oe=1048576,Re=async(e,t)=>{const{install:n}=await b.prompt([{type:"confirm",name:"install",message:"Install dependencies?",default:!0}]);if(!n){if(w.resolve(e)!==w.resolve(t)){const o=w.relative(t,e);console.log(f.cyan(`Run "cd ${o}" to change to the application folder.`))}return}try{const t=X();t.start("Installing dependencies...");try{const s=await ce(e);if(t.stop(),0!==s)return void o(`install exited with code ${s}`)}catch(i){throw t.stop(),i}}catch(i){return s(i),void console.error(f.red(i instanceof Error?i.message:"Failed to run install."))}const{migrate:r}=await b.prompt([{type:"confirm",name:"migrate",message:"Create or update database?",default:!0}]);if(!r)return;o(`Running dashnex db migrate in ${e}`);try{const t=X();t.start("Creating or updating database...");try{const n=await((e,t,o)=>{const n=`${w.join(e,"node_modules",".bin")}${w.delimiter}${process.env.PATH??""}`,r={...process.env,PATH:n};return new Promise((n,a)=>{const i=v(t,{cwd:e,shell:!0,stdio:"inherit",env:o?{...r,...o}:r});i.on("error",e=>{s(e),a(e)}),i.on("close",e=>{n(null===e?0:e)})})})(e,"dashnex db migrate");if(t.stop(),0!==n)return void o(`dashnex db migrate exited with code ${n}`)}catch(i){throw t.stop(),i}}catch(i){return s(i),void console.error(f.red("Failed to run dashnex db migrate."))}const{dev:a}=await b.prompt([{type:"confirm",name:"dev",message:"Run development server?",default:!0}]);if(a)try{await ae(e)}catch(i){s(i),console.error(f.red(i instanceof Error?i.message:"Failed to run dev server."))}},Be=(e,t)=>{if(e.length!==t.length)return`local ${e.length} bytes, incoming ${t.length} bytes`;const o=e.length;for(let s=0;s<o;s+=1)if(e[s]!==t[s]){return`size ${o} matches; first diff at byte ${s} (local=0x${e[s].toString(16).padStart(2,"0")} incoming=0x${t[s].toString(16).padStart(2,"0")})`}return`size ${o} matches; no byte difference (unexpected)`},_e=async(e,t)=>{let o;try{o=await g.lstat(e)}catch{return{kind:"new"}}if(o.isDirectory())return{kind:"different",reason:"target is a directory"};if(o.size!==t.length)return{kind:"different",reason:`local ${o.size} bytes, incoming ${t.length} bytes`};const s=await g.readFile(e);return 0===Buffer.compare(s,t)?{kind:"identical"}:{kind:"different",reason:Be(s,t)}},ze=async(e,t,o)=>{console.log(`\n${f.bold(t)}`);try{const t=await g.readFile(e);if(t.length>Oe||o.length>Oe||ke(t)||ke(o))return void console.log(f.dim(`(no preview: ${Be(t,o)})`));console.log(Se(t.toString("utf8"),o.toString("utf8")))}catch(n){s(n),console.log(f.dim("(no preview available)"))}},Le=async e=>{const{choice:t}=await b.prompt([{type:"expand",name:"choice",message:`Overwrite ${e}?`,default:"y",choices:[{key:"y",name:"yes (overwrite this file)",value:"yes"},{key:"n",name:"no (keep local version)",value:"no"},{key:"a",name:"overwrite all remaining differing files",value:"all"},{key:"s",name:"skip all remaining differing files",value:"skip"}]}]);return t},Ue=async(e,t)=>{const o=t?f.yellow(" (has local changes!)"):"",{choice:s}=await b.prompt([{type:"expand",name:"choice",message:`Delete ${e}? It no longer exists in the application.${o}`,default:"y",choices:[{key:"y",name:"yes (delete this file)",value:"yes"},{key:"n",name:"no (keep this file)",value:"no"},{key:"a",name:"delete all remaining removed files",value:"all"},{key:"s",name:"keep all remaining removed files",value:"skip"}]}]);return s},Me=async(e,t)=>{let o=w.dirname(e);const s=w.resolve(t);for(;w.resolve(o)!==s;)try{if((await g.readdir(o)).length>0)return;await g.rmdir(o),o=w.dirname(o)}catch{return}},We=(e,t)=>w.resolve(e)===w.resolve(t)?"Application is successfully pulled.":`Application is successfully pulled into ${w.relative(t,e)}.`,Je=class{async execute(e={}){o("Pull flow started");const t=await h();if(!t)return;const r=process.cwd();let a;if(e.folder)a=e.folder.trim()||".",o(`Folder argument provided: ${a}`);else{o("No folder argument, prompting user");const{folder:e}=await b.prompt([{type:"input",name:"folder",message:"Which folder to pull the application to?",default:"."}]);a=e.trim()||"."}const c=w.resolve(r,a);o(`Target folder resolved: ${c}`);try{const e=await g.lstat(c).catch(()=>null);e&&!e.isDirectory()&&(console.error(f.red(`Cannot pull into ${c}: path exists and is not a directory.`)),process.exit(1)),await g.ensureDir(c),o(`Target folder ensured: ${c}`)}catch(p){s(p);const e=p instanceof Error?p.message:"Unknown error";console.error(f.red(`Cannot create folder ${c}: ${e}`)),process.exit(1)}const l=w.join(P.tmpdir(),`dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`),d=X();d.start("Analyzing local files...");try{const n=await Ie(c),a=await Ne(c,{includeGenerated:!0});o(`Local manifest has ${Object.keys(a).length} files`),d.text("Pulling application...");const i=await this.download(t.token,a);d.text("Downloading..."),await g.writeFile(l,i.buffer),o(`Zip written to temp file (${i.viaV2?"v2 diff":"legacy full"} pull)`),d.text("Extracting...");const u=new A(l).getEntries().filter(e=>!e.isDirectory);let h=null;const m=u.find(e=>".dashnex/pull.json"===e.entryName);if(m)try{h=JSON.parse(m.getData().toString("utf8"))}catch(p){s(p)}const y=u.filter(e=>".dashnex"!==e.entryName&&!e.entryName.startsWith(".dashnex/"));o(`Zip has ${y.length} file entries${h?`, meta commit ${h.commit}`:", no meta"}`);const $=e.yes||"1"===process.env.DASHNEX_CI;let v=$?"all":"prompt",k=!1,x=0,b=0,S=0,E=0,D=0;const T=async(e,t,o)=>{try{return await g.ensureDir(w.dirname(e)),await g.writeFile(e,t),o&&await g.chmod(e,o),!0}catch(p){return s(p),!1}};for(const e of y){const t=e.getData(),s=w.join(c,e.entryName),n=w.relative(c,s)||e.entryName,r=(e.header.attr??0)>>16&4095,a=await _e(s,t);if(o(`Entry ${e.entryName}: ${a.kind}${a.reason?` (${a.reason})`:""}`),"identical"===a.kind){E+=1;continue}if("new"===a.kind){await T(s,t,r)?x+=1:D+=1;continue}let i=!1;if("all"===v)i=!0;else if("none"===v)i=!1;else{d.stop(),await ze(s,n,t),k||(console.log(f.dim("Y = yes (default), n = skip, a = overwrite all, s = skip all, h = help")),k=!0);const r=await Le(n);o(`Entry ${e.entryName}: user chose ${r}`),"yes"===r?i=!0:"no"===r?i=!1:"all"===r?(v="all",i=!0):(v="none",i=!1),d.start("Extracting...")}i?await T(s,t,r)?b+=1:D+=1:S+=1}d.stop();let C=0,P=0;if(h&&h.deleted.length>0){const e=await this.applyDeletions(c,h.deleted,n,$);C=e.deleted,P=e.kept}h&&(E+=Math.max(0,Object.keys(h.manifest).length-y.length)),o(`Pull summary: ${x} new, ${b} overwritten, ${S} skipped, ${C} deleted, ${E} unchanged, ${D} failed`);const j=[`${x} new`,`${b} overwritten`,`${S} skipped`,`${E} unchanged`];if((C>0||P>0)&&j.splice(3,0,`${C} deleted`),D>0&&j.push(`${D} failed`),console.log(f.cyan(`Pulled: ${j.join(", ")}`)),h){const e=await Ne(c,{includeGenerated:!0});await Fe(c,h.commit,e)}w.resolve(c)!==w.resolve(r)?(console.log(f.green(We(c,r))),process.chdir(c),o(`Changed working directory to ${c}`)):console.log(f.green(We(c,r))),D>0&&(console.error(f.red(`${D} file(s) failed to write. See DEBUG output for details.`)),process.exit(1)),$?o("Non-interactive mode (--yes or --ci), skipping post-pull child commands"):await Re(c,r)}catch(p){if(d.stop(),s(p),p instanceof Error&&p.message.startsWith("EXIT:"))throw p;if(n(p))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}finally{await g.remove(l).catch(()=>{}),o("Temp zip removed")}}async download(e,t){const s=`${u()}/business/v1/cli/v2/pull`;o(`POST ${s}`);const n=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({manifest:t})});if(o(`v2 pull response: ${n.status}`),n.ok)return{buffer:Buffer.from(await n.arrayBuffer()),viaV2:!0};const r=await n.json().catch(()=>({}));var a,i;a=n.status,i=r,404===a&&"string"==typeof i.message&&i.message.startsWith("Cannot ")||this.failFromResponse(n.status,r),o("v2 pull endpoint unavailable, falling back to legacy pull");const c=`${u()}/business/v1/cli/pull`;o(`GET ${c}`);const l=await fetch(c,{headers:{Authorization:`Bearer ${e}`}});if(o(`Legacy pull response: ${l.status}`),!l.ok){const e=await l.json().catch(()=>({}));this.failFromResponse(l.status,e)}return{buffer:Buffer.from(await l.arrayBuffer()),viaV2:!1}}failFromResponse(e,t){let o="Failed to pull application.";if(401===e||403===e)o="Please run 'dashnex login' to authenticate.";else if(404===e)o="Business has no application. Run 'dashnex app create' to create one.";else{const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(t);e&&(o=e)}console.error(f.red(o)),process.exit(1)}async applyDeletions(e,t,n,r){let a=0,i=0;if(!n){const e=t.slice(0,20);console.log(f.yellow("These local files do not exist in the application (kept — no sync state yet):"));for(const t of e)console.log(f.yellow(` ${t}`));return t.length>e.length&&console.log(f.yellow(` … and ${t.length-e.length} more`)),{deleted:a,kept:t.length}}let c=r?"all":"prompt";for(const d of t){if(!(d in n.manifest)){o(`Deletion candidate ${d} was never synced, keeping`);continue}const t=w.join(e,d),r=await g.readFile(t).catch(()=>null);if(null===r)continue;const p=Ce(r)!==n.manifest[d];let u=!1;if("all"===c)u=!0;else if("none"===c)u=!1;else{const e=await Ue(d,p);o(`Deletion ${d}: user chose ${e}`),"yes"===e?u=!0:"no"===e?u=!1:"all"===e?(c="all",u=!0):(c="none",u=!1)}if(u)try{await g.remove(t),await Me(t,e),a+=1}catch(l){s(l),i+=1}else i+=1}return{deleted:a,kept:i}}},He=async e=>{const t=`${u()}/business/v1/agreements/payment-processing/status`;o(`GET ${t}`);const s=await fetch(t,{headers:{Authorization:`Bearer ${e}`}});if(o(`Payment agreement response: ${s.status}`),!s.ok)return!0;if((await s.json().catch(()=>({}))).hasAccepted)return!0;const n=`${await(async()=>"qa"===process.env.APPLICATION_ENV?"https://business.qa.dashnex.com":"https://business.dashnex.com")()}/money/agreement/`;return console.error(f.red(`You need to accept Master Payment Processing Agreement first by visiting this link: ${n}`)),!1},Ge=async(e,t={})=>{const n=t.ciMode??!1;return!!(await He(e))&&!!(await(async(e,t)=>{const n=`${u()}/business/v1/tos/latest`;o(`GET ${n}`);const r=await fetch(n,{headers:{Authorization:`Bearer ${e}`}});if(o(`TOS latest response: ${r.status}`),r.ok)return!0;if(404!==r.status)return!0;const a=`${u()}/business/v1/tos/current`;o(`GET ${a}`);const i=await fetch(a,{headers:{Authorization:`Bearer ${e}`}});if(o(`TOS current response: ${i.status}`),!i.ok)return o("Could not fetch current TOS, skipping check"),!0;const c=await i.json().catch(()=>({})),l=c.version??"unknown";if(c.text&&(console.log(f.dim("─".repeat(60))),console.log(f.bold(`Terms of Service ${l}`)),console.log(f.dim("─".repeat(60))),console.log(c.text.replace(/<br\s*\/?>/gi,"\n").replace(/<\/p>/gi,"\n\n").replace(/<\/div>/gi,"\n").replace(/<\/li>/gi,"\n").replace(/<[^>]+>/g,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/ /g," ").replace(/\n{3,}/g,"\n\n").trim()),console.log(f.dim("─".repeat(60)))),t)return console.log(f.yellow(`By proceeding you accept the Terms of Service ${l}.`)),!0;const{accept:d}=await b.prompt([{type:"confirm",name:"accept",message:`By creating an application you accept the Terms of Service ${l}. Continue?`,default:!0}]);if(!d)return console.log(f.yellow("Cancelled.")),!1;try{const t=`${u()}/business/v1/tos/${c.id}/accept`;o(`POST ${t}`),await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({})})}catch(p){s(p)}return!0})(e,n))},Xe=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,qe=e=>new Promise(t=>setTimeout(t,e)),Ye=e=>e.appFullCustomDomain?e.appFullCustomDomain:e.subdomain?"qa"===(process.env.APPLICATION_ENV??"")?`https://${e.subdomain}.dashnex.dev`:`https://${e.subdomain}.dashnexcloud.com`:null,Ve=3e5,Ke=99,Ze=e=>new Promise(t=>setTimeout(t,e)),Qe=async(e,t,n,r)=>{const a=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${t}`,Accept:"text/event-stream"}});if(o(`SSE connection opened: ${a.status}`),!a.ok){const e=a.status;let t="Failed to start deployment.";if(401===e||403===e)t="Please run 'dashnex login' to authenticate.";else try{const e=await a.text(),o=JSON.parse(e);"string"==typeof o.error?t=o.error:"string"==typeof o.message&&(t=o.message)}catch{}throw console.error(f.red(t)),new Error(`HTTP ${e}: ${t}`)}if(!a.body)throw new Error("Response body is null");o("SSE connection established successfully"),r?.();const i=a.body.getReader();try{for await(const e of async function*(e){const t=new TextDecoder("utf-8");let o="";for(;;){const{value:s,done:n}=await e.read();if(n)break;o+=t.decode(s,{stream:!0});const r=o.split("\n");o=r.pop()||"";let a="";for(const e of r)e.startsWith("data: ")?a=e.slice(6):""===e&&a&&(yield{data:a},a="")}if(o.startsWith("data: ")){const e=o.slice(6);e&&(yield{data:e})}}(i))try{if(!e.data){o("Received empty SSE event");continue}const t=JSON.parse(e.data);o(`SSE message received: ${JSON.stringify(t)}`),n(t)}catch(c){s(c),o(`Failed to parse SSE event data: ${e.data}`)}}finally{i.releaseLock()}},et=class{async execute(e={}){o("Deploy flow started");const t=await h();if(!t)return;if(!1!==e.check?(o("Running pre-deploy checks"),await(new $e).run(!1)||(console.error(f.red("Deploy aborted: checks failed. Re-run with --no-check to skip.")),process.exit(1))):o("Skipping pre-deploy checks (--no-check)"),!(await Ge(t.token,{ciMode:!0})))return;const r=`${a()}/business/v1/applications/deploy`;o(`GET ${r} (SSE)`);let c=0,l="",d=!1,p=0,u=!1;const m=X();let g=0,w=0,y=null;const $=(e,t)=>{const o=Math.round(e);m.text(`[${o}%] ${t}`)},v=()=>{k();const e=c,t=Date.now(),o=Ve*((Ke-e)/Ke);y=setInterval(()=>{const s=Date.now()-t;let n;if(w>t){const e=Date.now()-w,t=Ke-g,o=3030.3030303030305*t;n=Math.min(g+e/o*t,Ke)}else n=Math.min(e+s/o*(Ke-e),Ke);n>c&&(c=n,$(c,l))},200)},k=()=>{y&&(clearInterval(y),y=null)};let x=!1;const b=e=>{if(u=!0,"string"==typeof e.error)return k(),m.stop(),console.error(f.red(e.error)),d=!0,void(x=!0);"string"==typeof e.message&&(l=e.message),"number"==typeof e.progress&&(g=e.progress,w=Date.now(),e.progress>c&&(c=e.progress)),"string"!=typeof e.message&&"number"!=typeof e.progress||(c>=100?(k(),m.stop(),d=!0):$(c,l))};for(;p<=5;)try{return u?$(c,l):m.start("Connecting..."),v(),await Qe(r,t.token,b,()=>{u||(m.text(`[${Math.round(c)}%] Deployment in progress...`),l="Deployment in progress...")}),k(),m.stop(),o("SSE connection closed"),x&&process.exit(1),void(d&&await this.showDeployedUrl(t.token))}catch(S){if(k(),s(S),o(`SSE connection error: ${S}`),d)return m.stop(),void(x&&process.exit(1));if(n(S))return m.stop(),void console.log(f.yellow(i));S instanceof Error&&S.message.startsWith("HTTP")&&(m.stop(),console.error(f.red("Could not complete deployment. Check your connection and try again.")),process.exit(1)),p++,p>5&&(m.stop(),console.error(f.red("Deployment failed. Maximum reconnection attempts exceeded.")),process.exit(1));const e=Math.min(1e3*Math.pow(2,p-1),3e4);o(`Connection error. Reconnecting in ${e/1e3}s... (attempt ${p}/5)`),await Ze(e)}}async showDeployedUrl(e){try{const t=`${u()}/business/v1/applications`;o(`GET ${t} (for deployed URL)`);const s=await fetch(t,{headers:{Authorization:`Bearer ${e}`}});if(!s.ok)return;const n=await s.json().catch(()=>({})),r=Ye({subdomain:n.subdomain,appFullCustomDomain:n.appFullCustomDomain});r&&console.log(`Application is deployed to ${f.cyan(r)}`)}catch(t){s(t)}}},tt=104857600,ot=class extends Error{},st=class extends Error{},nt=class extends Error{},rt="/business/v1/cli/tail-token";async function at(e,t={}){const o=`${u()}${rt}${t.debug?"?debug=1":""}`;try{await fetch(o,{method:"DELETE",headers:{Authorization:`Bearer ${e}`}})}catch{}}var it=["log","info","warn","error","debug"],ct={error:4,warn:3,log:2,info:2,debug:1};function lt(e){return"string"==typeof e&&it.includes(e)}function dt(e){return e.map(e=>"string"==typeof e?e:function(e){try{return JSON.stringify(e)}catch{return String(e)}}(e)).join(" ")}function pt(e){return e<10?`0${e}`:String(e)}function ut(e){const t=new Date(e);return`${pt(t.getHours())}:${pt(t.getMinutes())}:${pt(t.getSeconds())}.${o=t.getMilliseconds(),o<10?`00${o}`:o<100?`0${o}`:String(o)}`;var o}function ht(e,t){if("string"==typeof e)return e;if("object"==typeof(o=e)&&null!==o&&"string"==typeof o.name&&"string"==typeof o.message&&Object.keys(o).every(e=>"name"===e||"message"===e||"stack"===e)){const t=`${e.name}: ${e.message}`;return e.stack?`${t}\n${e.stack}`:t}var o;return O(e,{colors:t,depth:2,breakLength:120})}var mt={error:e=>f.red(e),warn:e=>f.yellow(e),debug:e=>f.dim(e),log:e=>e,info:e=>e};function ft(e,t){const o=t.color?f.dim(ut(e.ts)):ut(e.ts),s=e.level.toUpperCase().padEnd(5),n=[o,t.color?mt[e.level](s):s];n.push(e.args.map(e=>ht(e,t.color)).join(" "));let r=n.join(" ");if(e.truncated){const e=" …(truncated)";r+=t.color?f.dim(e):e}return r}function gt(e,t,o){let s;try{s=JSON.parse(e)}catch{return{kind:"malformed",raw:e}}if("object"!=typeof s||null===s||!lt(s.level))return{kind:"malformed",raw:e};const n=s;return Array.isArray(n.args)||(n.args=void 0===n.args?[]:[n.args]),r=n.level,a=t.minLevel,ct[r]>=ct[a]&&function(e,t){return!t||t.test(dt(e.args))}(n,t.filter)?{kind:"line",line:ft(n,o)}:{kind:"filtered"};var r,a}var wt=new Set([4401,4403]);var yt=[{label:"node_modules",segments:["node_modules"],description:"installed dependencies"},{label:"node_modules/.vite",segments:["node_modules",".vite"],description:"vite cache"},{label:"node_modules/.vite-temp",segments:["node_modules",".vite-temp"],description:"vite cache"},{label:"src/dashnex",segments:["src","dashnex"],description:"generated module registry"},{label:"dist",segments:["dist"],description:"build output"}],$t={label:".wrangler",segments:[".wrangler"],description:"local database"},vt=async e=>{await R(e,{recursive:!0,force:!0,maxRetries:5,retryDelay:100})},kt={name:"app",description:"Manage DashNex applications (should be executed in application folder)",handler:{execute:async()=>{}},options:[],subcommands:[{name:"create",description:"Create a new DashNex application",handler:new class{async pollApplicationCreated(e,t,n=5e3,r=6e4){const a=Date.now();let i=0;for(;Date.now()-a<r;){i++,o(`Polling application status (attempt ${i})...`);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(c){s(c),o(`Poll attempt ${i} failed with network error`)}await qe(n)}return{ready:!1}}async execute(e={}){o("Create flow started");const t=await h();if(!t)return;o("Auth valid, checking if business already has application");const r=X(),a=`${u()}/business/v1/applications`;o(`GET ${a}`),r.start("Checking application status...");try{const e=await fetch(a,{headers:{Authorization:`Bearer ${t.token}`}});r.stop(),o(`Applications response: ${e.status}`),o(`Applications response body: ${await e.text()}`),404!==e.status&&(console.error(f.red("Business already has an application. Run 'dashnex app pull' to pull the existing one.")),process.exit(1))}catch(y){if(r.stop(),s(y),y instanceof Error&&y.message.startsWith("EXIT:"))throw y;if(n(y))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}o("No existing application, checking agreements");const c=void 0!==e.subdomain;if(!(await Ge(t.token,{ciMode:c})))return;let l;o("Agreements checked, proceeding to subdomain prompt");const d=e=>`${u()}/business/v1/applications/check-subdomain?subdomain=${encodeURIComponent(e)}`;let p=e.subdomain?.trim();for(;;){if(p)l=p,p=void 0,o(`Subdomain provided via flag: ${l}`);else{if(c)return console.error(f.red("Subdomain is not available.")),void process.exit(1);{const{sub:e}=await b.prompt([{type:"input",name:"sub",message:"Pick your subdomain:",validate:e=>!!e.trim()||"Subdomain is required"}]);l=e.trim()}}o(`Checking subdomain: ${l}`);const e=d(l);o(`GET ${e}`),r.start("Checking subdomain...");try{const s=await fetch(e,{headers:{Authorization:`Bearer ${t.token}`}});r.stop(),o(`Check subdomain response: ${s.status}`);const n=await s.text();if(o(`Check subdomain response body: ${n}`),!s.ok){const e=s.headers.get("content-type")??"";let t="Failed to check subdomain availability.";if(401!==s.status&&403!==s.status||(t="Please run 'dashnex login' to authenticate.",console.error(f.red(t)),process.exit(1)),e.includes("application/json")){const e=JSON.parse(n);o(`Check subdomain error body: ${JSON.stringify(e)}`);const s=Xe(e);s&&(t=s)}console.error(f.red(t));continue}const a=JSON.parse(n);if(o(`Check subdomain parsed body: ${JSON.stringify(a)}`),a.available){o(`Subdomain ${l} is available`);break}console.error(f.red("Subdomain is not available. Please choose a different one."))}catch(y){if(r.stop(),s(y),y instanceof Error&&y.message.startsWith("EXIT:"))throw y;if(n(y))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}const m=`${u()}/business/v1/cli/create`;o(`POST ${m}`);const g={subdomain:l};e.template&&(g.template=e.template,o(`Template: ${e.template}`));const w=JSON.stringify(g);o(`Create request body: ${w}`),r.start("Creating application...");try{const a=await fetch(m,{method:"POST",headers:{Authorization:`Bearer ${t.token}`,"Content-Type":"application/json"},body:w});r.stop(),o(`Create response: ${a.status}`);const l=await a.text();if(o(`Create response body: ${l}`),503===a.status){o("Create returned 503, polling for application creation..."),r.start("Waiting for application to be ready...");const e=`${u()}/business/v1/applications`,s=await this.pollApplicationCreated(e,t.token);r.stop(),s.ready||(console.error(f.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(f.red("Application creation failed. Please try again.")),process.exit(1)),o("Application found after polling")}else if(!a.ok){const e=a.headers.get("content-type")??"";let t="Failed to create application.";if(401===a.status||403===a.status)t="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e=JSON.parse(l);o(`Create error body parsed: ${JSON.stringify(e)}`);const s=Xe(e);s&&(t=s)}console.error(f.red(t)),process.exit(1)}let d;if(o("Application created successfully"),!0===e.pull?(d=!0,o("--pull flag set, pulling without prompting")):!1===e.pull?(d=!1,o("--no-pull flag set, skipping pull")):c?(d=!1,o("CI mode (--subdomain), skipping pull")):(d=(await b.prompt([{type:"confirm",name:"pull",message:"Pull the application locally?",default:!0}])).pull,o(`User chose to pull: ${d}`)),d){o("Running pull flow");try{await(new Je).execute()}catch(y){if(s(y),y instanceof Error&&y.message.startsWith("EXIT:"))throw y;if(n(y))return void console.log(f.yellow(i));console.error(f.red("Failed to pull application."))}}else console.log(f.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(y){if(r.stop(),s(y),y instanceof Error&&y.message.startsWith("EXIT:"))throw y;if(n(y))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[{flags:"-t, --template <template>",description:"Template (name@branch-or-tag)",defaultValue:"webapp-base@live"},{flags:"--subdomain <subdomain>",description:"Subdomain (CI mode, skip prompts)"},{flags:"--pull",description:"Auto-pull after creation (skip prompt)"},{flags:"--no-pull",description:"Skip the pull prompt and show the success message"}]},{name:"pull",description:"Pull existing application to specified folder",handler:new Je,arguments:[{name:"folder",description:"Target folder (defaults to interactive prompt)"}],options:[{flags:"-y, --yes",description:"Skip prompts and child commands (CI mode)"}]},{name:"push",description:"Push local application to DashNex",handler:new class{async execute(e={}){o("Push flow started"),await g.pathExists(w.join(process.cwd(),"dashnex.json"))||(console.error(f.red("Not a DashNex application folder (dashnex.json not found).")),console.error(f.red("Run this command from your application root.")),process.exit(1)),!1!==e.check?(o("Running pre-push checks"),await(new $e).run(!1)||(console.error(f.red("Push aborted: checks failed. Re-run with --no-check to skip.")),process.exit(1))):o("Skipping pre-push checks (--no-check)");const t=await h();if(!t)return;const r=process.cwd(),a=X();try{a.start("Analyzing local files...");const s=await Ie(r),n=await Ne(r);o(`Local manifest has ${Object.keys(n).length} files`),a.text("Comparing with remote...");const i=await this.requestPlan(t.token,n,s?.commit);a.stop(),"legacy"===i?(o("v2 push endpoints unavailable, falling back to legacy zip push"),await this.pushLegacy(t.token,r,a)):await this.pushDiff(t.token,r,n,i,e,a),await this.promptAndDeploy(e)}catch(c){if(a.stop(),s(c),c instanceof Error&&c.message.startsWith("EXIT:"))throw c;if(n(c))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}async requestPlan(e,t,s){const n=`${u()}/business/v1/cli/v2/push/plan`;o(`POST ${n}`);const r=await fetch(n,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({manifest:t,lastSyncCommit:s})});if(o(`Plan response: ${r.status}`),r.ok)return await r.json();const a=await r.json().catch(()=>({}));if(((e,t)=>404===e&&"string"==typeof t.message&&t.message.startsWith("Cannot "))(r.status,a))return"legacy";this.failFromResponse(r.status,a,"Failed to push application.")}async pushDiff(e,t,s,n,r,a){let i=n;if(i.remoteChanged.length>0&&!r.force){console.error(f.red("Remote application has changes you don't have locally:"));for(const e of i.remoteChanged.slice(0,20))console.error(f.red(` ${e}`));i.remoteChanged.length>20&&console.error(f.red(` … and ${i.remoteChanged.length-20} more`)),console.error(f.red("Run 'dashnex app pull' first, or push anyway with --force.")),process.exit(1)}const c=i.upload.filter(e=>e in s);let l=i.delete;if(0!==c.length||0!==l.length){if(l.length>0){const e=await Ie(t),s=r.yes||"1"===process.env.DASHNEX_CI;console.log(f.yellow(`${l.length} file(s) will be deleted from the application repository:`));for(const t of l.slice(0,20))console.log(f.yellow(` ${t}`));if(l.length>20&&console.log(f.yellow(` … and ${l.length-20} more`)),!s){const{confirmDelete:t}=await b.prompt([{type:"confirm",name:"confirmDelete",message:"Delete these files from the remote repository?",default:null!==e}]);t||(o("User declined remote deletions, pushing without them"),l=[])}}if(0!==c.length||0!==l.length)for(let n=1;;n+=1){a.start(1===n?"Uploading changes...":"Remote changed, retrying...");const d=await this.applyPush(e,t,c,l,i.baseCommit);if(a.stop(),"ok"===d.status){await Fe(t,d.commit,s);const e=[`${c.length} file(s) updated`];return l.length>0&&e.push(`${l.length} deleted`),console.log(f.cyan(`Pushed: ${e.join(", ")}`)),void console.log(f.green("Application pushed successfully."))}n>=2&&(console.error(f.red("Remote repository keeps changing, please retry the push.")),process.exit(1)),o("Push apply conflicted, re-planning");const p=await Ie(t),u=await this.requestPlan(e,s,p?.commit);"legacy"===u&&(console.error(f.red("Failed to push application.")),process.exit(1)),u.remoteChanged.length>0&&!r.force&&(console.error(f.red("Remote application changed during the push. Run 'dashnex app pull' first, or push with --force.")),process.exit(1)),i=u}else console.log(f.green("Everything up to date."))}else console.log(f.green("Everything up to date."))}async applyPush(e,t,s,n,r){const a=new A;for(const o of s){const e=w.join(t,...o.split("/")),s=w.posix.dirname(o);a.addLocalFile(e,"."===s?"":s,w.posix.basename(o))}const i=w.join(P.tmpdir(),`dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);try{a.writeZip(i),o(`Diff zip written to ${i} (${s.length} files, ${n.length} deletions)`),await this.ensureZipWithinLimit(i);const t=`${u()}/business/v1/cli/v2/push/apply`;o(`POST ${t}`);const c=await g.readFile(i),l=new FormData;l.append("file",new Blob([c],{type:"application/zip"}),"diff.zip"),l.append("baseCommit",r),l.append("deletions",JSON.stringify(n));const d=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${e}`},body:l});if(o(`Apply response: ${d.status}`),409===d.status)return{status:"conflict"};if(!d.ok){const e=await d.json().catch(()=>({}));this.failFromResponse(d.status,e,"Failed to push application.")}return{status:"ok",commit:(await d.json().catch(()=>({}))).commit??""}}finally{await g.remove(i).catch(()=>{})}}async pushLegacy(e,t,s){const n=await Ne(t),r=Object.keys(n);o(`Legacy push including ${r.length} files`);const a=new A;for(const o of r){const e=w.join(t,...o.split("/")),s=w.posix.dirname(o);a.addLocalFile(e,"."===s?"":s,w.posix.basename(o))}const i=w.join(P.tmpdir(),`dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);try{s.start("Packaging application..."),a.writeZip(i),o(`Zip written to ${i}`),await this.ensureZipWithinLimit(i);const t=`${u()}/business/v1/cli/push`;o(`POST ${t}`);const n=await g.readFile(i),r=new FormData;r.append("file",new Blob([n],{type:"application/zip"}),"archive.zip"),s.text("Uploading...");const c=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${e}`},body:r});if(s.stop(),o(`Response: ${c.status}`),!c.ok){const e=await c.json().catch(()=>({}));this.failFromResponse(c.status,e,"Failed to push application.")}console.log(f.green("Application pushed successfully."))}finally{await g.remove(i).catch(()=>{}),o("Temp zip removed")}}async ensureZipWithinLimit(e){const{size:t}=await g.stat(e);if(t>tt){const e=e=>(e/1048576).toFixed(1);console.error(f.red(`Push package is ${e(t)} MB — the maximum is ${e(tt)} MB.`)),console.error(f.red("Remove or .gitignore large files and try again.")),process.exit(1)}}failFromResponse(e,t,o){let s=o;if(401===e||403===e)s="Please run 'dashnex login' to authenticate.";else{const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(t);e&&(s=e)}console.error(f.red(s)),process.exit(1)}async promptAndDeploy(e){let t;void 0!==e.deploy?(t=e.deploy,o(`Deploy decided via flag: ${t}`)):"1"===process.env.DASHNEX_CI?(t=!1,o("CI mode: skipping deploy prompt (use --deploy to deploy)")):(t=(await b.prompt([{type:"confirm",name:"deploy",message:"Deploy the application?",default:!0}])).deploy,o(`User chose to deploy: ${t}`)),t&&(o("Running deploy flow"),await(new et).execute())}},options:[{flags:"--deploy",description:"Auto-deploy after push (skip prompt)"},{flags:"--no-deploy",description:"Skip deploy prompt"},{flags:"--no-check",description:"Skip pre-push checks"},{flags:"-f, --force",description:"Push even when the remote has changes you don't have locally"},{flags:"-y, --yes",description:"Skip prompts (CI mode)"}]},{name:"delete",description:"Delete application from DashNex",handler:new class{async execute(e={}){if(o("Delete flow started"),!e.yes){const{confirm:e}=await b.prompt([{type:"confirm",name:"confirm",message:"Are you sure you want to delete this application? This action cannot be undone.",default:!1}]);if(!e)return o("User cancelled deletion"),void console.log(f.yellow("Deletion cancelled."))}const t=await h();if(!t)return;const r=`${u()}/business/v1/applications`;o(`DELETE ${r}`);const a=X();a.start("Deleting application...");try{const e=await fetch(r,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(a.stop(),o(`Response: ${e.status}`),!e.ok){const t=e.headers.get("content-type")??"";let o="Failed to delete application.";if(401===e.status||403===e.status)o="Please run 'dashnex login' to authenticate.";else if(t.includes("application/json")){const t=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(await e.json().catch(()=>({})));t&&(o=t)}console.error(f.red(o)),process.exit(1)}const s=await e.json().catch(()=>({})),n="string"==typeof s.message?s.message:"Application deleted successfully.";o("Application deleted successfully"),console.log(f.green(n))}catch(c){if(a.stop(),s(c),c instanceof Error&&c.message.startsWith("EXIT:"))throw c;if(n(c))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[{flags:"-y, --yes",description:"Skip confirmation prompt"}]},{name:"deploy",description:"Deploy application to DashNex",handler:new et,options:[{flags:"--no-check",description:"Skip pre-deploy checks"}]},{name:"status",description:"Show current application status",handler:new class{async execute(){o("Status flow started");const e=await h();if(!e)return;const t=`${u()}/business/v1/applications`;o(`GET ${t}`);const r=X();r.start("Fetching application status...");try{const s=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}});if(r.stop(),o(`Response: ${s.status}`),404===s.status)return void console.log(f.yellow("Application not created"));if(!s.ok){const e=s.headers.get("content-type")??"";let t="Failed to fetch application status.";if(401===s.status||403===s.status)t="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e=await s.json().catch(()=>({}));"string"==typeof e.error?t=e.error:"string"==typeof e.message&&(t=e.message)}console.error(f.red(t)),process.exit(1)}const n=await s.json().catch(()=>({}));o(`Application body: ${JSON.stringify(n)}`);const a=n.status??"unknown";console.log(`Status: ${f.cyan(a)}`);const i=Ye(n);i&&console.log(`URL: ${f.cyan(i)}`)}catch(a){if(r.stop(),s(a),a instanceof Error&&a.message.startsWith("EXIT:"))throw a;if(n(a))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[]},{name:"clear",description:"Delete local dependencies, caches and build output",handler:new class{async execute(e={}){o("Clear flow started");const t=process.cwd();g.pathExistsSync(w.join(t,"dashnex.json"))||(console.error(f.red("Run this command in a DashNex Web application folder.")),process.exit(1));const r=e.yes||"1"===process.env.DASHNEX_CI;o(`Non-interactive: ${r}`);try{const e=await this.collectTargets(t),n=w.join(t,...$t.segments),i=await g.pathExists(n),c=i&&!r;if(i&&r&&e.push($t),0===e.length&&!c)return void console.log(f.yellow("Nothing to clear."));const l=[];if(e.length>0){console.log("The following will be deleted:");for(const t of e)console.log(` ${f.cyan(t.label)} ${f.dim(`(${t.description})`)}`);if(!r){const{confirm:e}=await b.prompt([{type:"confirm",name:"confirm",message:"Delete these files?",default:!1}]);if(!e)return o("User cancelled clear"),void console.log(f.yellow("Cancelled."))}for(const n of e){const e=w.join(t,...n.segments);o(`Removing ${e}`);try{await vt(e),console.log(f.green(`Removed ${n.label}`))}catch(a){s(a),l.push(n.label)}}}if(c){const{confirm:e}=await b.prompt([{type:"confirm",name:"confirm",message:"Delete the local database as well?",default:!1}]);if(e){o(`Removing ${n}`);try{await vt(n),console.log(f.green(`Removed ${$t.label}`))}catch(a){s(a),l.push($t.label)}}}if(l.length>0){for(const e of l)console.error(f.red(`Could not delete ${e} — it may be in use. Stop 'dashnex dev' and try again.`));process.exit(1)}console.log(f.green("Clear complete. Run 'dashnex install' to set the application up again."))}catch(a){if(a instanceof Error&&a.message.startsWith("EXIT:"))throw a;if(s(a),n(a))return void console.log(f.yellow(i));throw a}}async collectTargets(e){const t=[];for(const o of yt)await g.pathExists(w.join(e,...o.segments))&&t.push(o);return t.filter(o=>!t.some(t=>t!==o&&((e,t)=>{const o=w.relative(t,e);return""!==o&&!o.startsWith("..")&&!w.isAbsolute(o)})(w.join(e,...o.segments),w.join(e,...t.segments))))}},options:[{flags:"-y, --yes",description:"Skip prompts and keep the local database (CI mode)"}]},{name:"tail",description:"Stream live console/debug logs from the deployed worker (Ctrl+C to stop)",handler:new class{async execute(e={}){const t=e.level??"log";lt(t)||(console.error(f.red(`Invalid --level '${e.level}'. Use one of: log, info, warn, error, debug.`)),process.exit(1));let n=null;if(e.filter)try{n=new RegExp(e.filter)}catch{console.error(f.red(`Invalid --filter regular expression: ${e.filter}`)),process.exit(1)}const r=await h();if(!r)return;const a=!!e.debug;let i;try{i=await async function(e,t={}){const s=`${u()}${rt}`;o(`POST ${s}${t.debug?" (debug)":""}`);const n=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({debug:!!t.debug})});if(o(`Response: ${n.status}`),401===n.status||403===n.status)throw new ot("Not authorised to tail this application.");if(404===n.status)throw new st("Business has no application, or the tail endpoint is unavailable.");if(!n.ok)throw new nt(`Failed to start tail (status ${n.status}).`);const r=await n.json().catch(()=>({}));if(!r.token||!r.workerUrl)throw new nt("Malformed tail-token response from the platform.");return{token:r.token,workerUrl:r.workerUrl,expiresAt:"number"==typeof r.expiresAt?r.expiresAt:0}}(r.token,{debug:a})}catch(k){s(k),k instanceof ot&&(console.error(f.red(k.message)),process.exit(1)),k instanceof st&&(console.error(f.red(k.message)),process.exit(2)),console.error(f.red(k instanceof Error?k.message:"Could not start tail. Check your connection.")),process.exit(3)}const c={color:!!process.stdout.isTTY},l={minLevel:t,filter:n},d=(p=i.workerUrl,`${/^(localhost|127\.0\.0\.1)/.test(p)?"ws":"wss"}://${p}/api/realtime/tail`);var p;const m=`dashnex.tail.token.${i.token}`;console.log(f.dim(`Tailing ${i.workerUrl} — Ctrl+C to stop.`)),a&&console.log(f.dim("Debug logging enabled on the worker (DEBUG=1); may take a minute to take effect."));let g=!1,w=Date.now();const y=setInterval(()=>{g||Date.now()-w>=3e4&&(console.log(f.dim("(no logs in 30s)")),w=Date.now())},5e3),$=new I(d,m,{WebSocket:F,connectionTimeout:1e4,maxReconnectionDelay:16e3}),v=async e=>{if(!g){g=!0,clearInterval(y);try{$.close()}catch{}await Promise.race([at(r.token,{debug:a}),new Promise(e=>setTimeout(e,3e3))]),process.exit(e)}};process.on("SIGINT",()=>{console.log(),v(0)}),$.binaryType="arraybuffer",$.addEventListener("message",e=>{w=Date.now();const t=gt("string"==typeof e.data?e.data:"",l,c);"line"===t.kind?console.log(t.line):"malformed"===t.kind&&console.error(f.dim(`[malformed frame] ${t.raw}`))}),$.addEventListener("close",e=>{if(!g)return wt.has(e.code)?(console.error(f.red("Tail token was rejected (auth). Exiting.")),void v(1)):void o(`socket closed (code ${e.code}); reconnecting…`)}),$.addEventListener("error",e=>{o(`socket error: ${e.message??"unknown"}`)}),await new Promise(()=>{})}},options:[{flags:"--level <level>",description:"Minimum level: log, info, warn, error, debug"},{flags:"--filter <regex>",description:"Only show records whose args match this regex"},{flags:"--debug",description:"Also enable DEBUG=1 on the worker so debug-gated logs are shown"}]}]},xt={name:"secrets",description:"Manage DashNex secrets",handler:{execute:async()=>{}},options:[],subcommands:[{name:"list",description:"List all secret names bound to the worker",handler:new class{async execute(){o("Secrets list flow started");const e=await h();if(!e)return;const t=`${u()}/business/v1/cli/secrets`;o(`GET ${t}`);const r=X();r.start("Fetching secrets...");try{const s=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}});if(r.stop(),o(`Response: ${s.status}`),!s.ok){let e="Failed to fetch secrets.";401===s.status||403===s.status?e="Please run 'dashnex login' to authenticate.":404===s.status?e="Business has no application.":502===s.status&&(e="Error fetching secrets. Make sure your application is deployed."),console.error(f.red(e)),process.exit(1)}const n=(await s.json()).secrets??[];if(0===n.length)return void console.log(f.yellow("No secrets set."));console.log(f.bold(`\nSecrets (${n.length}):\n`)),n.forEach(({name:e})=>{console.log(` ${f.cyan(e)}`)}),console.log("")}catch(a){if(r.stop(),s(a),a instanceof Error&&a.message.startsWith("EXIT:"))throw a;if(n(a))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},options:[]},{name:"set",description:"Create or update a secret (prompts for missing KEY/VALUE)",handler:new class{async execute(e={}){o("Secrets set flow started");let{name:t,value:r}=e;const a="1"===process.env.DASHNEX_CI;t||(a&&(console.error(f.red("--name is required in CI mode (--ci).")),process.exit(1)),t=(await b.prompt([{type:"input",name:"name",message:"Secret name:",validate:e=>""!==e.trim()||"Name is required"}])).name.trim()),r||(a&&(console.error(f.red("--value is required in CI mode (--ci).")),process.exit(1)),r=(await b.prompt([{type:"password",name:"value",message:`Enter value for ${t}:`,mask:"*",validate:e=>""!==e||"Value is required"}])).value);const c=await h();if(!c)return;const l=`${u()}/business/v1/cli/secrets`;o(`PUT ${l}`);const d=X();d.start(`Setting secret '${t}'...`);try{const e=await fetch(l,{method:"PUT",headers:{Authorization:`Bearer ${c.token}`,"Content-Type":"application/json"},body:JSON.stringify({name:t,value:r})});if(d.stop(),o(`Response: ${e.status}`),!e.ok){let t="Failed to set secret.";401===e.status||403===e.status?t="Please run 'dashnex login' to authenticate.":400===e.status?t="Missing or empty name / value.":404===e.status?t="Business has no application.":502===e.status&&(t="Error setting secret. Make sure your application is deployed."),console.error(f.red(t)),process.exit(1)}o(`Secret '${t}' set successfully`),console.log(f.green(`Secret '${t}' set successfully.`))}catch(p){if(d.stop(),s(p),p instanceof Error&&p.message.startsWith("EXIT:"))throw p;if(n(p))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},arguments:[{name:"name",description:"Secret name (KEY)"},{name:"value",description:"Secret value"}],options:[{flags:"--name <name>",description:"Secret name (alternative to positional KEY)"},{flags:"--value <value>",description:"Secret value (alternative to positional VALUE)"}]},{name:"delete",description:"Delete a secret (prompts for missing KEY)",handler:new class{async execute(e={}){o("Secrets delete flow started");let{name:t}=e;const r="1"===process.env.DASHNEX_CI;t||(r&&(console.error(f.red("--name is required in CI mode (--ci).")),process.exit(1)),t=(await b.prompt([{type:"input",name:"name",message:"Secret name:",validate:e=>""!==e.trim()||"Name is required"}])).name.trim());const a=await h();if(!a)return;const c=`${u()}/business/v1/cli/secrets/${encodeURIComponent(t)}`;o(`DELETE ${c}`);const l=X();l.start(`Deleting secret '${t}'...`);try{const e=await fetch(c,{method:"DELETE",headers:{Authorization:`Bearer ${a.token}`}});if(l.stop(),o(`Response: ${e.status}`),!e.ok){let o="Failed to delete secret.";401===e.status||403===e.status?o="Please run 'dashnex login' to authenticate.":404===e.status?o=`Secret '${t}' not found.`:502===e.status&&(o="Error deleting secret. Make sure your application is deployed."),console.error(f.red(o)),process.exit(1)}o(`Secret '${t}' deleted successfully`),console.log(f.green(`Secret '${t}' deleted.`))}catch(d){if(l.stop(),s(d),d instanceof Error&&d.message.startsWith("EXIT:"))throw d;if(n(d))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}},arguments:[{name:"name",description:"Secret name (KEY)"}],options:[{flags:"--name <name>",description:"Secret name (alternative to positional KEY)"}]}]},bt=[{name:"version",description:"Display the version of the CLI",handler:new class{async execute(e){const t=w.dirname(y(import.meta.url)),o=JSON.parse(x.readFileSync(w.resolve(t,"..","..","package.json"),"utf8"));console.log(o.version)}},options:[]},{name:"login",description:"Log in to your DashNex account",handler:new q,options:[{flags:"--business <id>",description:"Business ID to select (skip prompt)"},{flags:"--no-browser",description:"Print the sign-in URL instead of opening a browser"}]},{name:"logout",description:"Log out and remove local credentials",handler:new class{async execute(){const e=r(process.cwd());if(await g.pathExists(e)){const t=await g.readJson(e);delete t.token,delete t.refreshToken,delete t.businessId,await g.writeJson(e,t,{spaces:2})}console.log(f.green("Logged out."))}},options:[]},{name:"whoami",description:"Show current logged-in business and user",handler:new class{async execute(){o("Whoami flow started");const e=await h();if(!e)return;const t=await c()?`${u()}/business/v1/`:`${l()}/users/v1/`;o(`GET ${t}`);const r=X();r.start("Fetching user info...");try{const s=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}});if(r.stop(),o(`Response: ${s.status}`),!s.ok){const e=s.headers.get("content-type")??"";let t="Failed to fetch user info.";if(401===s.status||403===s.status)t="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e="string"==typeof(a=await s.json().catch(()=>({}))).error?a.error:"string"==typeof a.message?a.message:void 0;e&&(t=e)}console.error(f.red(t)),process.exit(1)}const n=await s.json().catch(()=>({})),i="string"==typeof n.name?n.name:"string"==typeof n.companyName?n.companyName:"Unknown",c=n.memberUser,l=c&&"string"==typeof c.fullName?c.fullName:c&&"string"==typeof c.name?c.name:"Unknown";o(`Whoami: ${i} (${l})`),console.log(f.green(`You are logged in as ${i} (${l})`))}catch(d){if(r.stop(),s(d),d instanceof Error&&d.message.startsWith("EXIT:"))throw d;if(n(d))return void console.log(f.yellow(i));console.error(f.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}var a}},options:[]},{name:"dev",description:"Run the development server (run in DashNex application folder)",handler:new class{async execute(e={}){o("Dev flow started");try{const t=await ae(process.cwd(),e);process.exit(t)}catch(t){if(t instanceof Error&&t.message.startsWith("EXIT:"))throw t;s(t),t instanceof Error?console.error(f.red(t.message)):console.error(f.red("Failed to run npm run dev")),process.exit(1)}}},options:[{flags:"-p, --port <port>",description:"Port for the dev server (default: PORT env or 3000)"},{flags:"-H, --host <host>",description:"Host to bind, e.g. 0.0.0.0 inside Docker (default: HOST env or localhost)"},{flags:"--https",description:"Serve over HTTPS with a trusted local certificate (mkcert)"}]},{name:"check",description:"Run build-time validations (run in DashNex application folder)",handler:new $e,options:[]},{name:"install",description:"Install dependencies using pnpm (preferred) or npm (run in DashNex application folder)",handler:new class{async execute(){o("Install flow started");const e=w.join(process.cwd(),"dashnex.json");await g.pathExists(e)||(console.error(f.red("Run this command in a DashNex Web application folder.")),process.exit(1));const t=process.cwd(),n=X();n.start("Installing dependencies...");try{const e=await ce(t);n.stop(),0!==e&&process.exit(e),console.log(f.green("Dependencies installed.")),process.exit(0)}catch(r){n.stop(),s(r),r instanceof Error?console.error(f.red(r.message)):console.error(f.red("Failed to run install")),process.exit(1)}}},options:[]},kt,xt];export{bt as default};
|
package/dist/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as s,t as e}from"./chunks/dashnex-
|
|
1
|
+
import{n as s,t as e}from"./chunks/dashnex-BWlu5keu.js";var n={name:s.name,version:s.version,description:s.description,...e};export{n as default};
|
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import { CliCommand } from '@dashnex/types';
|
|
2
|
+
/** Hardcoded regex patterns; lines matching any of these are filtered from npm run dev output. */
|
|
3
|
+
export declare const DEV_OUTPUT_FILTER_PATTERNS: string[];
|
|
2
4
|
export interface DevCommandOptions {
|
|
3
5
|
https?: boolean;
|
|
6
|
+
/** Port for the dev server; falls back to the PORT env var, then the framework default (3000). */
|
|
7
|
+
port?: string | number;
|
|
8
|
+
/** Host/interface to bind (e.g. 0.0.0.0 inside Docker); falls back to the HOST env var. */
|
|
9
|
+
host?: string;
|
|
4
10
|
}
|
|
11
|
+
/** Resolve the bind host from `--host`, then `HOST`; undefined keeps the framework default (localhost). */
|
|
12
|
+
export declare const resolveDevHost: (options: DevCommandOptions, env?: NodeJS.ProcessEnv) => string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the port to pass to the dev server from `--port`, then `PORT`.
|
|
15
|
+
* Returns undefined when neither is set so the framework default applies.
|
|
16
|
+
* @throws When the value is not a valid TCP port
|
|
17
|
+
*/
|
|
18
|
+
export declare const resolveDevPort: (options: DevCommandOptions, env?: NodeJS.ProcessEnv) => number | undefined;
|
|
5
19
|
/**
|
|
6
20
|
* Run dev server in the given directory. Single source of truth for dev logic.
|
|
7
21
|
* @param cwd - Directory (project root) to run dev in
|
|
8
|
-
* @param options - Dev options (
|
|
22
|
+
* @param options - Dev options (port, https)
|
|
9
23
|
* @returns Exit code (0 = success)
|
|
10
|
-
* @throws When not a DashNex project, package.json missing, no dev script, or npm unavailable
|
|
24
|
+
* @throws When not a DashNex project, package.json missing, no dev script, invalid port, or npm unavailable
|
|
11
25
|
*/
|
|
12
26
|
export declare const runDevInDir: (cwd: string, options?: DevCommandOptions) => Promise<number>;
|
|
13
27
|
export declare class DevCommand implements CliCommand {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trusted local HTTPS certificates for `dashnex dev --https`.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors what `next dev --experimental-https` does: use mkcert (from PATH, or a
|
|
5
|
+
* copy downloaded once into ~/.dashnex/mkcert) to install a local CA and issue a
|
|
6
|
+
* certificate for localhost into `<app>/.dashnex/certificates/` (the app's .dashnex
|
|
7
|
+
* folder is never committed). The certificate is reused on later runs as long as it
|
|
8
|
+
* is still valid for the requested hosts.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MKCERT_VERSION = "v1.4.4";
|
|
11
|
+
export declare const DEFAULT_CERT_DIR: string;
|
|
12
|
+
export interface LocalCertificates {
|
|
13
|
+
key: string;
|
|
14
|
+
cert: string;
|
|
15
|
+
rootCA?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface MkcertLookupOptions {
|
|
18
|
+
cacheDir?: string;
|
|
19
|
+
fetchImpl?: typeof fetch;
|
|
20
|
+
/** Returns the mkcert command to use when it is already installed, or null. */
|
|
21
|
+
lookupPath?: () => string | null;
|
|
22
|
+
log?: (message: string) => void;
|
|
23
|
+
}
|
|
24
|
+
export interface EnsureCertificatesOptions {
|
|
25
|
+
/** Extra host name to include in the certificate (e.g. myapp.local). */
|
|
26
|
+
host?: string;
|
|
27
|
+
/** Directory (relative to cwd) where the pem files live. Defaults to ".dashnex/certificates". */
|
|
28
|
+
certDir?: string;
|
|
29
|
+
log?: (message: string) => void;
|
|
30
|
+
/** Override how the mkcert binary is located (tests). */
|
|
31
|
+
resolveMkcert?: () => Promise<string>;
|
|
32
|
+
}
|
|
33
|
+
export declare const getMkcertBinaryName: (platform?: string, arch?: string) => string;
|
|
34
|
+
export declare const getMkcertCacheDir: () => string;
|
|
35
|
+
/**
|
|
36
|
+
* Locate mkcert: PATH first, then the DashNex cache, then download the pinned
|
|
37
|
+
* release from GitHub into the cache.
|
|
38
|
+
*/
|
|
39
|
+
export declare const findOrDownloadMkcert: (options?: MkcertLookupOptions) => Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Names the certificate must cover: always localhost + loopback IPs, plus the bind host when
|
|
42
|
+
* it is something else (a LAN IP, a hostname, or 0.0.0.0 / :: when the server listens on all
|
|
43
|
+
* interfaces, e.g. inside Docker — mkcert issues IP SANs for those too).
|
|
44
|
+
*/
|
|
45
|
+
export declare const certificateHosts: (bindHost?: string) => string[];
|
|
46
|
+
/** Add `entry` to .gitignore (if the file exists). Returns true when a line was appended. */
|
|
47
|
+
export declare const ensureGitignoreEntry: (cwd: string, entry: string) => Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* Make sure a trusted certificate for localhost exists under `<cwd>/certificates`.
|
|
50
|
+
* Returns null when HTTPS could not be set up so the caller can fall back to http.
|
|
51
|
+
*/
|
|
52
|
+
export declare const ensureLocalCertificates: (cwd: string, options?: EnsureCertificatesOptions) => Promise<LocalCertificates | null>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "dashnex",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
|
+
"dashnex": {
|
|
6
|
+
"minAppVersion": "1.1.0"
|
|
7
|
+
},
|
|
5
8
|
"description": "Command-line interface for DashNex framework",
|
|
6
9
|
"homepage": "https://dashnex.io",
|
|
7
10
|
"type": "module",
|
|
@@ -41,11 +44,12 @@
|
|
|
41
44
|
"fs-extra": "^11.3.5",
|
|
42
45
|
"ignore": "^7.0.5",
|
|
43
46
|
"inquirer": "^13.4.3",
|
|
47
|
+
"jiti": "^2.7.0",
|
|
44
48
|
"partysocket": "^1.3.0",
|
|
45
49
|
"ws": "^8.21.0"
|
|
46
50
|
},
|
|
47
51
|
"devDependencies": {
|
|
48
|
-
"@dashnex/types": "^1.
|
|
52
|
+
"@dashnex/types": "^1.5.0",
|
|
49
53
|
"@types/adm-zip": "^0.5.8",
|
|
50
54
|
"@types/fs-extra": "^11.0.4",
|
|
51
55
|
"@types/inquirer": "^9.0.9",
|
|
@@ -55,7 +59,7 @@
|
|
|
55
59
|
"rollup-plugin-preserve-directives": "^0.4.0",
|
|
56
60
|
"terser": "^5.47.1",
|
|
57
61
|
"typescript": "^5.9.3",
|
|
58
|
-
"vite": "^8.0.
|
|
62
|
+
"vite": "^8.0.8",
|
|
59
63
|
"vite-plugin-dts": "5.0.0-beta.6",
|
|
60
64
|
"vitest": "^4.1.6"
|
|
61
65
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import e from"chalk";import s from"fs-extra";import n from"path";var r=e=>e instanceof Error&&("ExitPromptError"===e.name||("AbortError"===e.name||("User force closed the prompt"===e.message||!!e.message.includes("prompt was closed")))),t="Command interrupted.",o=e=>{const s=e.toLowerCase();return s.includes("token")||s.includes("password")||"auth_code"===s},a=e=>{if(null===e||"object"!=typeof e)return e;if(Array.isArray(e))return e.map(a);const s={};for(const[n,r]of Object.entries(e))o(n)?s[n]="[REDACTED]":s[n]=a(r);return s},i=s=>{process.env.DEBUG&&console.log(e.dim(s))},c=(s,n)=>{if(process.env.DEBUG){const r=a(n);console.log(e.dim(`${s}: ${JSON.stringify(r,null,2)}`))}},p=s=>{process.env.DEBUG&&s instanceof Error&&(console.log(e.dim(`Error: ${s.message}`)),s.stack&&console.log(e.dim(`Stack: ${s.stack}`)))},l=()=>"qa"===process.env.APPLICATION_ENV?"https://api.qa.dashnex.com":"https://api.dashnex.com",u=()=>process.env.DASHNEX_BUSINESS_API_URL?process.env.DASHNEX_BUSINESS_API_URL.replace(/\/+$/,""):"qa"===process.env.APPLICATION_ENV?"https://api.business.qa.dashnex.com":"https://api.business.dashnex.com",d={qa:"https://app.qa.dashnex.com",live:"https://app.dashnex.com"},f={qa:"hacmrrpg1wnjv12oq22p8lfm",live:""},h=()=>process.env.DASHNEX_ID_URL?process.env.DASHNEX_ID_URL.replace(/\/+$/,""):d[process.env.APPLICATION_ENV||"live"]||d.live,m=()=>process.env.DASHNEX_OAUTH_CLIENT_ID?process.env.DASHNEX_OAUTH_CLIENT_ID:f[process.env.APPLICATION_ENV||"live"]??"",v={qa:"https://3atfynmu2w.us-east-1.awsapprunner.com",live:"https://rkwbzvhcwh.us-east-1.awsapprunner.com"},k=()=>v[process.env.APPLICATION_ENV||"live"]||v.live,E=async(e,s={})=>{const n=await fetch(e,{...s,headers:{"Content-Type":"application/json",...s.headers}});return{response:n,body:await n.json().catch(()=>({}))}},N=process.env.DASHNEX_DIR||".dashnex",T=e=>n.join(e,N,"settings.json"),g=()=>{console.error(e.red("Please run 'dashnex login' to authenticate.")),console.error(e.green(`If you don't have an account yet, create one at ${h()}`))},w=e=>Boolean(e.idToken),x=async e=>{let r=n.resolve(e);for(;;){const e=T(r);if(await s.pathExists(e))return e;const t=n.dirname(r);if(t===r)return null;r=t}},y=e=>{try{const s=e.split(".");if(3!==s.length)return null;const n=JSON.parse(Buffer.from(s[1],"base64url").toString());return void 0!==n.id?String(n.id):null}catch{return null}},A=(e,s=30)=>{try{const n=e.split(".");if(3!==n.length)return!1;const r=JSON.parse(Buffer.from(n[1],"base64url").toString());if("number"!=typeof r.exp)return!1;const t=Math.floor(Date.now()/1e3),o=r.exp-s<=t;return o&&i(`Token expired or expiring soon (exp: ${r.exp}, now: ${t})`),o}catch{return!1}},S=async(e,n,r)=>{const t=w(r)?`${u()}/business/v1/token/refresh`:`${l()}/auth/v1/token/refresh`;i(`POST ${t}`);const{response:o,body:a}=await E(t,{method:"POST",body:JSON.stringify({refreshToken:e})});if(i(`Response: ${o.status}`),c("Refresh response",a),!o.ok)return i("Token refresh failed"),null;const p=a;if(!p.token||!p.refreshToken)return i("Refresh response missing tokens"),null;const d=await s.readJson(n);return d.token=p.token,d.refreshToken=p.refreshToken,await s.writeJson(n,d,{spaces:2}),i("Updated settings.json with new tokens"),_(p.token,p.refreshToken,n,{...d,token:p.token,refreshToken:p.refreshToken})},_=async(e,s,n,r)=>{if(A(e)){if(!s)return i("Token expired, no refreshToken available"),null;i("Token expired, refreshing proactively");try{return await S(s,n,r)}catch(o){return p(o),null}}const t=w(r)?`${u()}/business/v1/`:`${l()}/users/v1/`;i(`GET ${t}`);try{const{response:o,body:a}=await E(t,{headers:{Authorization:`Bearer ${e}`}});if(i(`Response: ${o.status}`),c("Validation response",a),o.ok){const s=a,n=s.name??s.companyName,t=r.businessId??(void 0!==s.id?String(s.id):void 0);if("string"==typeof n&&void 0!==t)return i(`Token valid, Already logged in as ${n}`),{token:e,businessId:String(t),userName:n}}return s?(i("Validation endpoint rejected token, refreshing"),await S(s,n,r)):(i("Token invalid, no refreshToken available"),null)}catch(o){return p(o),null}},I=async(e=process.cwd())=>{const n=await x(e);if(!n)return!1;try{return w(await s.readJson(n))}catch{return!1}},D=async(n={})=>{const{exitOnFailure:r=!0,dashnexPath:t}=n,o=process.env.DASHNEX_TOKEN;if(o)return A(o)?(i("DASHNEX_TOKEN is expired"),console.error(e.red("DASHNEX_TOKEN is expired. Provide a freshly issued token.")),r&&process.exit(1),null):(i("Using DASHNEX_TOKEN from environment"),{token:o,businessId:y(o)??""});const a=t??await x(process.cwd());if(!a||!(await s.pathExists(a)))return i(".dashnex/settings.json not found"),r&&(g(),process.exit(1)),null;let c;i(`Found settings at ${a}`);try{c=await s.readJson(a)}catch{return i("settings.json parse failed"),r&&(g(),process.exit(1)),null}const{token:p,refreshToken:l,businessId:u}=c;if(!p)return i("settings.json missing token"),r&&(g(),process.exit(1)),null;const d=await _(p,l,a,c);return d||(r&&(g(),process.exit(1)),null)};export{E as a,h as c,i as d,p as f,r as h,T as i,k as l,t as m,I as n,l as o,c as p,N as r,u as s,D as t,m as u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e={$schema:"https://json.schemastore.org/package",name:"@dashnex/cli",version:"0.6.0",description:"Command-line interface for DashNex framework",homepage:"https://dashnex.io",type:"module",bin:{dashnex:"bin/dashnex"},engines:{node:">=22"},exports:{".":{types:"./dist/server.d.ts",default:"./dist/server.js"},"./client":{types:"./dist/client.d.ts",default:"./dist/client.js"}},scripts:{build:"vite build",dev:"vite build --watch --mode development",test:"vitest run"},keywords:["dashnex","cli"],author:"Dashnex Team",license:"UNLICENSED",dependencies:{"adm-zip":"^0.5.17",chalk:"^5.6.2",commander:"^14.0.3",diff:"^8.0.4",dotenv:"^17.4.2","fs-extra":"^11.3.5",ignore:"^7.0.5",inquirer:"^13.4.3",partysocket:"^1.3.0",ws:"^8.21.0"},devDependencies:{"@dashnex/types":"^1.4.4","@types/adm-zip":"^0.5.8","@types/fs-extra":"^11.0.4","@types/inquirer":"^9.0.9","@types/node":"^25.7.0","@types/ws":"^8.18.1",eslint:"^9.39.4","rollup-plugin-preserve-directives":"^0.4.0",terser:"^5.47.1",typescript:"^5.9.3",vite:"^8.0.12","vite-plugin-dts":"5.0.0-beta.6",vitest:"^4.1.6"},publishConfig:{registry:"https://npm.dashnex.com/"},files:["dist","bin","AGENTS.md","CLAUDE.md"]},s={};export{e as n,s as t};
|