@dashnex/cli 0.5.2 → 0.5.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +1 -161
  3. package/dist/client.js +1 -12
  4. package/dist/commands/create.js +1 -134
  5. package/dist/commands/delete.js +1 -51
  6. package/dist/commands/deploy.js +1 -0
  7. package/dist/commands/dev.js +1 -0
  8. package/dist/commands/index.js +1 -68
  9. package/dist/commands/install.js +1 -0
  10. package/dist/commands/login.js +1 -262
  11. package/dist/commands/logout.js +1 -16
  12. package/dist/commands/pull.js +1 -102
  13. package/dist/commands/push.js +1 -108
  14. package/dist/commands/version.js +1 -10
  15. package/dist/commands/whoami.js +1 -52
  16. package/dist/dashnex.json.js +1 -5
  17. package/dist/lib/api.js +1 -23
  18. package/dist/lib/debug.js +1 -45
  19. package/dist/lib/errors.js +1 -0
  20. package/dist/lib/spinner.js +1 -0
  21. package/dist/package.json.js +1 -15
  22. package/dist/server.js +1 -12
  23. package/dist/services/auth.js +1 -116
  24. package/dist/src/cli.d.ts +2 -0
  25. package/dist/src/client.d.ts +3 -0
  26. package/dist/src/commands/create.d.ts +11 -0
  27. package/dist/src/commands/delete.d.ts +4 -0
  28. package/dist/src/commands/deploy.d.ts +4 -0
  29. package/dist/src/commands/dev.d.ts +4 -0
  30. package/dist/src/commands/index.d.ts +3 -0
  31. package/dist/src/commands/install.d.ts +4 -0
  32. package/dist/src/commands/login.d.ts +8 -0
  33. package/dist/src/commands/logout.d.ts +4 -0
  34. package/dist/src/commands/pull.d.ts +6 -0
  35. package/dist/src/commands/push.d.ts +4 -0
  36. package/dist/src/commands/version.d.ts +5 -0
  37. package/dist/src/commands/whoami.d.ts +4 -0
  38. package/dist/src/lib/api.d.ts +8 -0
  39. package/dist/src/lib/debug.d.ts +3 -0
  40. package/dist/src/lib/errors.d.ts +2 -0
  41. package/dist/src/lib/spinner.d.ts +6 -0
  42. package/dist/src/server.d.ts +3 -0
  43. package/dist/src/services/auth.d.ts +10 -0
  44. package/package.json +5 -6
  45. package/dist/cli.js.map +0 -1
  46. package/dist/client.js.map +0 -1
  47. package/dist/commands/create.js.map +0 -1
  48. package/dist/commands/delete.js.map +0 -1
  49. package/dist/commands/index.js.map +0 -1
  50. package/dist/commands/login.js.map +0 -1
  51. package/dist/commands/logout.js.map +0 -1
  52. package/dist/commands/pull.js.map +0 -1
  53. package/dist/commands/push.js.map +0 -1
  54. package/dist/commands/version.js.map +0 -1
  55. package/dist/commands/whoami.js.map +0 -1
  56. package/dist/dashnex.json.js.map +0 -1
  57. package/dist/lib/api.js.map +0 -1
  58. package/dist/lib/debug.js.map +0 -1
  59. package/dist/package.json.js.map +0 -1
  60. package/dist/server.js.map +0 -1
  61. package/dist/services/auth.js.map +0 -1
@@ -1,262 +1 @@
1
- import inquirer from "inquirer";
2
- import chalk from "chalk";
3
- import fs from "fs-extra";
4
- import path from "path";
5
- import { debug, debugJson, debugError } from "../lib/debug.js";
6
- import { getApiBase, apiFetch } from "../lib/api.js";
7
- import { ensureLoggedIn } from "../services/auth.js";
8
- const extractErrorMessage = (body) => {
9
- if (typeof body.error === "string") return body.error;
10
- if (typeof body.message === "string") return body.message;
11
- return void 0;
12
- };
13
- const handleApiError = (response, body, defaultMessage) => {
14
- const apiMessage = extractErrorMessage(body);
15
- let message = defaultMessage;
16
- if (response.status === 429) {
17
- message = "Too many requests. Please wait a moment and try again.";
18
- } else if (response.status >= 500) {
19
- message = "DashNex API is temporarily unavailable. Please try again later.";
20
- } else if (apiMessage) {
21
- message = apiMessage;
22
- }
23
- console.error(chalk.red(message));
24
- process.exit(1);
25
- };
26
- class LoginCommand {
27
- async execute() {
28
- debug("Login flow started");
29
- const cwd = process.cwd();
30
- const dashnexPath = path.join(cwd, ".dashnex");
31
- const session = await ensureLoggedIn({ exitOnFailure: false, dashnexPath });
32
- if (session) {
33
- console.log(chalk.green(`Already logged in as ${session.userName ?? "user"}`));
34
- return;
35
- }
36
- try {
37
- const { username, password } = await inquirer.prompt([
38
- {
39
- type: "input",
40
- name: "username",
41
- message: "Email:",
42
- validate: (input) => input.trim() ? true : "Email is required"
43
- },
44
- {
45
- type: "password",
46
- name: "password",
47
- message: "Password:",
48
- mask: "*",
49
- validate: (input) => input ? true : "Password is required"
50
- }
51
- ]);
52
- const trimmedUsername = username.trim();
53
- debug("Email provided");
54
- debug(`POST ${getApiBase()}/auth/v1/login`);
55
- const { response: loginResponse, body: loginBody } = await apiFetch(
56
- `${getApiBase()}/auth/v1/login`,
57
- {
58
- method: "POST",
59
- body: JSON.stringify({ username: trimmedUsername, password })
60
- }
61
- );
62
- debug(`Response: ${loginResponse.status}`);
63
- debugJson("Login response", loginBody);
64
- if (!loginResponse.ok) {
65
- debugError(new Error(`Login failed: ${loginResponse.status} ${JSON.stringify(loginBody)}`));
66
- if (loginResponse.status === 401) {
67
- console.error(chalk.red("Invalid username or password."));
68
- process.exit(1);
69
- }
70
- handleApiError(
71
- loginResponse,
72
- loginBody,
73
- "Login failed. Please try again."
74
- );
75
- }
76
- const loginData = loginBody;
77
- if (loginData.two_fa_required && loginData.token) {
78
- debug(`2FA required, type: ${loginData.type || "unknown"}`);
79
- const token = await this.handle2FA(loginData.token, loginData.type);
80
- if (!token) process.exit(1);
81
- await this.completeLogin(token, dashnexPath);
82
- } else if (loginData.token && loginData.refreshToken) {
83
- await this.completeLogin(
84
- { token: loginData.token, refreshToken: loginData.refreshToken },
85
- dashnexPath
86
- );
87
- } else {
88
- console.error(chalk.red("Invalid response from server. Please try again."));
89
- process.exit(1);
90
- }
91
- } catch (error) {
92
- debugError(error);
93
- console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
94
- process.exit(1);
95
- }
96
- }
97
- async handle2FA(interimToken, type) {
98
- if (type === "sms") {
99
- debug(`POST ${getApiBase()}/auth/v1/two-fa/sms`);
100
- try {
101
- const { response } = await apiFetch(`${getApiBase()}/auth/v1/two-fa/sms`, {
102
- method: "POST",
103
- headers: { Authorization: `Bearer ${interimToken}` },
104
- body: JSON.stringify({})
105
- });
106
- debug(`Response: ${response.status}`);
107
- } catch (err) {
108
- debugError(err);
109
- console.error(chalk.red("Could not send SMS code. Check your connection and try again."));
110
- return null;
111
- }
112
- }
113
- const typeLabel = type === "sms" ? "Check your phone for the code" : "Check your authenticator app for the code";
114
- const maxAttempts = 3;
115
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
116
- const { auth_code } = await inquirer.prompt([
117
- {
118
- type: "input",
119
- name: "auth_code",
120
- message: `Enter code (${typeLabel}):`,
121
- validate: (input) => input.trim() ? true : "Code is required"
122
- }
123
- ]);
124
- debug(`POST ${getApiBase()}/auth/v1/2fa-check`);
125
- try {
126
- const { response, body } = await apiFetch(`${getApiBase()}/auth/v1/2fa-check`, {
127
- method: "POST",
128
- headers: { Authorization: `Bearer ${interimToken}` },
129
- body: JSON.stringify({ auth_code: auth_code.trim() })
130
- });
131
- debug(`Response: ${response.status}`);
132
- debugJson("2FA check response", body);
133
- if (response.ok) {
134
- const data = body;
135
- if (data.token && data.refreshToken) {
136
- return { token: data.token, refreshToken: data.refreshToken };
137
- }
138
- }
139
- if (response.status >= 400 && response.status < 500) {
140
- console.error(chalk.red("Invalid code. Please try again."));
141
- if (attempt < maxAttempts) {
142
- const { retry } = await inquirer.prompt([
143
- {
144
- type: "confirm",
145
- name: "retry",
146
- message: "Try again?",
147
- default: true
148
- }
149
- ]);
150
- if (!retry) return null;
151
- }
152
- continue;
153
- }
154
- handleApiError(response, body, "2FA verification failed. Please try again.");
155
- } catch (err) {
156
- debugError(err);
157
- console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
158
- return null;
159
- }
160
- }
161
- return null;
162
- }
163
- async completeLogin(tokens, dashnexPath) {
164
- debug(`GET ${getApiBase()}/users/v1/business/my`);
165
- const { response: bizResponse, body: bizBody } = await apiFetch(
166
- `${getApiBase()}/users/v1/business/my`,
167
- {
168
- headers: { Authorization: `Bearer ${tokens.token}` }
169
- }
170
- );
171
- debug(`Response: ${bizResponse.status}`);
172
- debugJson("Businesses response", bizBody);
173
- if (!bizResponse.ok) {
174
- debugError(new Error(`Business fetch failed: ${bizResponse.status}`));
175
- handleApiError(
176
- bizResponse,
177
- bizBody,
178
- "Failed to fetch businesses. Please try again."
179
- );
180
- }
181
- const businesses = this.parseBusinesses(bizBody);
182
- debug(`Businesses: ${businesses.length}`);
183
- if (businesses.length === 0) {
184
- console.error(chalk.red("No businesses found for your account."));
185
- process.exit(1);
186
- }
187
- let selected;
188
- if (businesses.length === 1) {
189
- selected = businesses[0];
190
- debug(`Selected business: ${selected.id} ${selected.name}`);
191
- } else {
192
- const { businessId } = await inquirer.prompt([
193
- {
194
- type: "select",
195
- name: "businessId",
196
- message: "Select business:",
197
- choices: businesses.map((b) => ({ name: b.name, value: b.id }))
198
- }
199
- ]);
200
- selected = businesses.find((b) => b.id === businessId) || businesses[0];
201
- debug(`Selected business: ${selected.id} ${selected.name}`);
202
- }
203
- debug(`GET ${getApiBase()}/business/v1/login?_switch_business=${selected.id}`);
204
- const { response: switchResponse, body: switchBody } = await apiFetch(
205
- `${getApiBase()}/business/v1/login?_switch_business=${selected.id}`,
206
- {
207
- headers: { Authorization: `Bearer ${tokens.token}` }
208
- }
209
- );
210
- debug(`Response: ${switchResponse.status}`);
211
- debugJson("Business login response", switchBody);
212
- if (!switchResponse.ok) {
213
- debugError(new Error(`Business login failed: ${switchResponse.status}`));
214
- handleApiError(
215
- switchResponse,
216
- switchBody,
217
- "Failed to switch to business. Please try again."
218
- );
219
- }
220
- const switchData = switchBody;
221
- if (!switchData.token || !switchData.refreshToken) {
222
- console.error(chalk.red("Invalid response from server. Please try again."));
223
- process.exit(1);
224
- }
225
- const config = {
226
- token: switchData.token,
227
- refreshToken: switchData.refreshToken,
228
- businessId: selected.id
229
- };
230
- await fs.writeJson(dashnexPath, config, { spaces: 2 });
231
- debug("Saved credentials to .dashnex");
232
- this.ensureGitignore();
233
- console.log(chalk.green(`Logged in as ${selected.name}`));
234
- }
235
- parseBusinesses(body) {
236
- const arr = Array.isArray(body) ? body : body.items ?? body.data ?? body.businesses ?? [];
237
- if (!Array.isArray(arr)) return [];
238
- return arr.map((item) => {
239
- if (item && typeof item === "object") {
240
- const o = item;
241
- const id = String(o.id ?? o.business_id ?? "").trim();
242
- const name = String(o.name ?? o.companyName ?? o.business_name ?? "Unknown").trim();
243
- if (id) return { id, name };
244
- }
245
- return null;
246
- }).filter((b) => b !== null);
247
- }
248
- async ensureGitignore() {
249
- const gitignorePath = path.join(process.cwd(), ".gitignore");
250
- if (!await fs.pathExists(gitignorePath)) return;
251
- let content = await fs.readFile(gitignorePath, "utf8");
252
- if (content.includes(".dashnex")) return;
253
- content = content.trimEnd();
254
- if (!content.endsWith("\n")) content += "\n";
255
- content += "\n.dashnex\n";
256
- await fs.writeFile(gitignorePath, content);
257
- }
258
- }
259
- export {
260
- LoginCommand
261
- };
262
- //# sourceMappingURL=login.js.map
1
+ import e from"inquirer";import s from"chalk";import r from"fs-extra";import t from"path";import{debug as o,debugJson as n,debugError as a}from"../lib/debug.js";import{getApiBase as i,apiFetch as c}from"../lib/api.js";import{ensureLoggedIn as u}from"../services/auth.js";import{createSpinner as l}from"../lib/spinner.js";import{isUserInterrupt as d,INTERRUPTED_MESSAGE as m}from"../lib/errors.js";const p=(e,r,t)=>{const o=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(r);let n=t;429===e.status?n="Too many requests. Please wait a moment and try again.":e.status>=500?n="DashNex API is temporarily unavailable. Please try again later.":o&&(n=o),console.error(s.red(n)),process.exit(1)};class h{async execute(){o("Login flow started");const r=process.cwd(),h=t.join(r,".dashnex"),f=await u({exitOnFailure:!1,dashnexPath:h});if(f)console.log(s.green(`Already logged in as ${f.userName??"user"}`));else try{const{username:r,password:t}=await e.prompt([{type:"input",name:"username",message:"Email:",validate:e=>!!e.trim()||"Email is required"},{type:"password",name:"password",message:"Password:",mask:"*",validate:e=>!!e||"Password is required"}]),u=r.trim();o("Email provided");const d=l();d.start("Logging in..."),o(`POST ${i()}/auth/v1/login`);const{response:m,body:f}=await c(`${i()}/auth/v1/login`,{method:"POST",body:JSON.stringify({username:u,password:t})});d.stop(),o(`Response: ${m.status}`),n("Login response",f),m.ok||(a(new Error(`Login failed: ${m.status} ${JSON.stringify(f)}`)),401===m.status&&(console.error(s.red("Invalid username or password.")),process.exit(1)),p(m,f,"Login failed. Please try again."));const g=f;if(g.two_fa_required&&g.token){o(`2FA required, type: ${g.type||"unknown"}`);const e=await this.handle2FA(g.token,g.type);e||process.exit(1),await this.completeLogin(e,h)}else g.token&&g.refreshToken?await this.completeLogin({token:g.token,refreshToken:g.refreshToken},h):(console.error(s.red("Invalid response from server. Please try again.")),process.exit(1))}catch(g){if(a(g),d(g))return void console.log(s.yellow(m));console.error(s.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}async handle2FA(r,t){if("sms"===t){o(`POST ${i()}/auth/v1/two-fa/sms`);try{const{response:e}=await c(`${i()}/auth/v1/two-fa/sms`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:JSON.stringify({})});o(`Response: ${e.status}`)}catch(l){return a(l),console.error(s.red("Could not send SMS code. Check your connection and try again.")),null}}const u="sms"===t?"Check your phone for the code":"Check your authenticator app for the code";for(let h=1;h<=3;h++){const{auth_code:t}=await e.prompt([{type:"input",name:"auth_code",message:`Enter code (${u}):`,validate:e=>!!e.trim()||"Code is required"}]);o(`POST ${i()}/auth/v1/2fa-check`);try{const{response:a,body:u}=await c(`${i()}/auth/v1/2fa-check`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:JSON.stringify({auth_code:t.trim()})});if(o(`Response: ${a.status}`),n("2FA check response",u),a.ok){const e=u;if(e.token&&e.refreshToken)return{token:e.token,refreshToken:e.refreshToken}}if(a.status>=400&&a.status<500){if(console.error(s.red("Invalid code. Please try again.")),h<3){const{retry:s}=await e.prompt([{type:"confirm",name:"retry",message:"Try again?",default:!0}]);if(!s)return null}continue}p(a,u,"2FA verification failed. Please try again.")}catch(l){return a(l),d(l)?(console.log(s.yellow(m)),null):(console.error(s.red("Could not reach DashNex API. Check your connection and try again.")),null)}}return null}async completeLogin(t,u){const d=l();d.start("Fetching businesses..."),o(`GET ${i()}/users/v1/business/my`);const{response:m,body:h}=await c(`${i()}/users/v1/business/my`,{headers:{Authorization:`Bearer ${t.token}`}});d.stop(),o(`Response: ${m.status}`),n("Businesses response",h),m.ok||(a(new Error(`Business fetch failed: ${m.status}`)),p(m,h,"Failed to fetch businesses. Please try again."));const f=this.parseBusinesses(h);let g;if(o(`Businesses: ${f.length}`),0===f.length&&(console.error(s.red("No businesses found for your account.")),process.exit(1)),1===f.length)g=f[0],o(`Selected business: ${g.id} ${g.name}`);else{const{businessId:s}=await e.prompt([{type:"select",name:"businessId",message:"Select business:",choices:f.map(e=>({name:e.name,value:e.id}))}]);g=f.find(e=>e.id===s)||f[0],o(`Selected business: ${g.id} ${g.name}`)}d.start("Switching business..."),o(`GET ${i()}/business/v1/login?_switch_business=${g.id}`);const{response:y,body:w}=await c(`${i()}/business/v1/login?_switch_business=${g.id}`,{headers:{Authorization:`Bearer ${t.token}`}});d.stop(),o(`Response: ${y.status}`),n("Business login response",w),y.ok||(a(new Error(`Business login failed: ${y.status}`)),p(y,w,"Failed to switch to business. Please try again."));const k=w;k.token&&k.refreshToken||(console.error(s.red("Invalid response from server. Please try again.")),process.exit(1));const b={token:k.token,refreshToken:k.refreshToken,businessId:g.id};await r.writeJson(u,b,{spaces:2}),o("Saved credentials to .dashnex"),this.ensureGitignore(),console.log(s.green(`Logged in as ${g.name}`))}parseBusinesses(e){const s=Array.isArray(e)?e:e.items??e.data??e.businesses??[];return Array.isArray(s)?s.map(e=>{if(e&&"object"==typeof e){const s=e,r=String(s.id??s.business_id??"").trim(),t=String(s.name??s.companyName??s.business_name??"Unknown").trim();if(r)return{id:r,name:t}}return null}).filter(e=>null!==e):[]}async ensureGitignore(){const e=t.join(process.cwd(),".gitignore");if(!(await r.pathExists(e)))return;let s=await r.readFile(e,"utf8");s.includes(".dashnex")||(s=s.trimEnd(),s.endsWith("\n")||(s+="\n"),s+="\n.dashnex\n",await r.writeFile(e,s))}}export{h as LoginCommand};
@@ -1,16 +1 @@
1
- import fs from "fs-extra";
2
- import path from "path";
3
- import chalk from "chalk";
4
- class LogoutCommand {
5
- async execute() {
6
- const dashnexPath = path.join(process.cwd(), ".dashnex");
7
- if (await fs.pathExists(dashnexPath)) {
8
- await fs.remove(dashnexPath);
9
- }
10
- console.log(chalk.green("Logged out."));
11
- }
12
- }
13
- export {
14
- LogoutCommand
15
- };
16
- //# sourceMappingURL=logout.js.map
1
+ import o from"fs-extra";import e from"path";import t from"chalk";class a{async execute(){const a=e.join(process.cwd(),".dashnex");await o.pathExists(a)&&await o.remove(a),console.log(t.green("Logged out."))}}export{a as LogoutCommand};
@@ -1,102 +1 @@
1
- import chalk from "chalk";
2
- import fs from "fs-extra";
3
- import path from "path";
4
- import os from "os";
5
- import inquirer from "inquirer";
6
- import AdmZip from "adm-zip";
7
- import { debug, debugError } from "../lib/debug.js";
8
- import { getBusinessApiBase } from "../lib/api.js";
9
- import { ensureLoggedIn } from "../services/auth.js";
10
- const extractErrorMessage = (body) => {
11
- if (typeof body.error === "string") return body.error;
12
- if (typeof body.message === "string") return body.message;
13
- return void 0;
14
- };
15
- const SUCCESS_MESSAGE_NEW = 'Application is successfully pulled. Run "npm install" or "pnpm install" to install dependencies and then "npx dashnex dev" to run it';
16
- const getSuccessMessageNew = (targetDir, cwd) => {
17
- if (path.resolve(targetDir) === path.resolve(cwd)) {
18
- return SUCCESS_MESSAGE_NEW;
19
- }
20
- const folder = path.relative(cwd, targetDir);
21
- return `Application is successfully pulled. Run 'cd ${folder}' then "pnpm install" or "npm install" to install dependencies and then "npx dashnex dev" to run it`;
22
- };
23
- class PullCommand {
24
- async execute() {
25
- debug("Pull flow started");
26
- const session = await ensureLoggedIn();
27
- if (!session) return;
28
- const cwd = process.cwd();
29
- const dashnexJsonPath = path.join(cwd, "dashnex.json");
30
- const hasApp = await fs.pathExists(dashnexJsonPath);
31
- debug(`Application check (dashnex.json): ${hasApp ? "present" : "absent"}`);
32
- let targetDir;
33
- if (hasApp) {
34
- targetDir = cwd;
35
- } else {
36
- debug("No application yet, prompting for folder");
37
- const { folder } = await inquirer.prompt([
38
- {
39
- type: "input",
40
- name: "folder",
41
- message: "Which folder to pull the application to?",
42
- default: "."
43
- }
44
- ]);
45
- const resolved = path.resolve(cwd, folder.trim() || ".");
46
- targetDir = resolved;
47
- await fs.ensureDir(targetDir);
48
- debug(`Target folder created/resolved: ${targetDir}`);
49
- }
50
- const url = `${getBusinessApiBase()}/business/v1/cli/pull`;
51
- debug(`GET ${url}`);
52
- const tempZipPath = path.join(
53
- os.tmpdir(),
54
- `dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
55
- );
56
- try {
57
- const response = await fetch(url, {
58
- headers: { Authorization: `Bearer ${session.token}` }
59
- });
60
- debug(`Response: ${response.status}`);
61
- if (!response.ok) {
62
- const contentType = response.headers.get("content-type") ?? "";
63
- let message = "Failed to pull application.";
64
- if (response.status === 401 || response.status === 403) {
65
- message = "Please run 'npx dashnex login' to authenticate.";
66
- } else if (response.status === 404) {
67
- message = "Business has no application. Run 'npx dashnex create' to create one.";
68
- } else if (contentType.includes("application/json")) {
69
- const body = await response.json().catch(() => ({}));
70
- const apiMessage = extractErrorMessage(body);
71
- if (apiMessage) message = apiMessage;
72
- }
73
- console.error(chalk.red(message));
74
- process.exit(1);
75
- }
76
- const buffer = Buffer.from(await response.arrayBuffer());
77
- await fs.writeFile(tempZipPath, buffer);
78
- debug("Zip written to temp file");
79
- const zip = new AdmZip(tempZipPath);
80
- zip.extractAllTo(targetDir, true);
81
- debug("Extracted to target folder");
82
- if (hasApp) {
83
- console.log(chalk.green(`Application pulled to ${targetDir}`));
84
- } else {
85
- console.log(chalk.green(getSuccessMessageNew(targetDir, cwd)));
86
- }
87
- } catch (error) {
88
- debugError(error);
89
- if (error instanceof Error && error.message.startsWith("EXIT:")) throw error;
90
- console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
91
- process.exit(1);
92
- } finally {
93
- await fs.remove(tempZipPath).catch(() => {
94
- });
95
- debug("Temp zip removed");
96
- }
97
- }
98
- }
99
- export {
100
- PullCommand
101
- };
102
- //# sourceMappingURL=pull.js.map
1
+ import e from"chalk";import t from"fs-extra";import o from"path";import r from"os";import{spawn as n}from"child_process";import i from"inquirer";import a from"adm-zip";import{debug as s,debugError as l}from"../lib/debug.js";import{getBusinessApiBase as c}from"../lib/api.js";import{ensureLoggedIn as d}from"../services/auth.js";import{createSpinner as p}from"../lib/spinner.js";import{isUserInterrupt as m,INTERRUPTED_MESSAGE as f}from"../lib/errors.js";const u=(e,t)=>new Promise((o,r)=>{const i=n(t,{cwd:e,shell:!0,stdio:"inherit"});i.on("error",e=>{l(e),r(e)}),i.on("close",e=>{o(null===e?0:e)})}),g=async(t,r)=>{const{install:a}=await i.prompt([{type:"confirm",name:"install",message:"Install dependencies?",default:!0}]);if(!a){if(o.resolve(t)!==o.resolve(r)){const n=o.relative(r,t);console.log(e.cyan(`Run "cd ${n}" to change to the application folder.`))}return}if(a){const o=await(async()=>{const e=e=>new Promise(t=>{const o=n(e,["--version"],{stdio:"ignore",shell:!0});o.on("error",()=>t(!1)),o.on("close",e=>t(0===e))});return await e("pnpm")?"pnpm":(await e("npm"),"npm")})();s(`Running ${o} install in ${t}`);try{const e=await u(t,`${o} install`);if(0!==e)return void s(`${o} install exited with code ${e}`)}catch(p){return l(p),void console.error(e.red(`Failed to run ${o} install.`))}}const{migrate:c}=await i.prompt([{type:"confirm",name:"migrate",message:"Create local database?",default:!0}]);if(c){s(`Running npx dashnex db migrate in ${t}`);try{const e=await u(t,"npx dashnex db migrate");if(0!==e)return void s(`dashnex db migrate exited with code ${e}`)}catch(p){return l(p),void console.error(e.red("Failed to run dashnex db migrate."))}}const{dev:d}=await i.prompt([{type:"confirm",name:"dev",message:"Run development server?",default:!0}]);if(d){s(`Running npm run dev in ${t}`);try{await u(t,"npm run dev")}catch(p){l(p),console.error(e.red("Failed to run dev server."))}}},h='Application is successfully pulled. Run "npm install" or "pnpm install" to install dependencies and then "npx dashnex dev" to run it';class w{async execute(n={}){s("Pull flow started");const u=await d();if(!u)return;const w=process.cwd();let v;if(n.folder)v=n.folder.trim()||".",s(`Folder argument provided: ${v}`);else{s("No folder argument, prompting user");const{folder:e}=await i.prompt([{type:"input",name:"folder",message:"Which folder to pull the application to?",default:"."}]);v=e.trim()||"."}const x=o.resolve(w,v);s(`Target folder resolved: ${x}`);const y=await t.pathExists(x);let $=!1;if(y)try{$=(await t.readdir(x)).length>0,s(`Folder exists: ${y}, has files: ${$}`)}catch(R){s(`Error reading folder: ${R}`)}if($){const{confirm:t}=await i.prompt([{type:"confirm",name:"confirm",message:"Existing files will be overwritten. Continue?",default:!0}]);if(!t)return s("User declined confirmation, exiting"),void console.log(e.yellow("Pull cancelled."));s("User confirmed overwrite")}await t.ensureDir(x),s(`Target folder ensured: ${x}`);const b=`${c()}/business/v1/cli/pull`;s(`GET ${b}`);const j=o.join(r.tmpdir(),`dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`),E=p();E.start("Pulling application...");try{const r=await fetch(b,{headers:{Authorization:`Bearer ${u.token}`}});if(s(`Response: ${r.status}`),!r.ok){E.stop();const t=r.headers.get("content-type")??"";let o="Failed to pull application.";if(401===r.status||403===r.status)o="Please run 'npx dashnex login' to authenticate.";else if(404===r.status)o="Business has no application. Run 'npx dashnex create' to create one.";else if(t.includes("application/json")){const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(await r.json().catch(()=>({})));e&&(o=e)}console.error(e.red(o)),process.exit(1)}E.text("Downloading...");const n=Buffer.from(await r.arrayBuffer());await t.writeFile(j,n),s("Zip written to temp file"),E.text("Extracting...");new a(j).extractAllTo(x,!0),E.stop(),s("Extracted to target folder"),o.resolve(x)!==o.resolve(w)?(console.log(e.green(((e,t)=>o.resolve(e)===o.resolve(t)?h:`Application is successfully pulled into ${o.relative(t,e)}.`)(x,w))),process.chdir(x),s(`Changed working directory to ${x}`)):console.log(e.green(h)),await g(x,w)}catch(R){if(E.stop(),l(R),R instanceof Error&&R.message.startsWith("EXIT:"))throw R;if(m(R))return void console.log(e.yellow(f));console.error(e.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}finally{await t.remove(j).catch(()=>{}),s("Temp zip removed")}}}export{w as PullCommand};
@@ -1,108 +1 @@
1
- import chalk from "chalk";
2
- import fs from "fs-extra";
3
- import path from "path";
4
- import os from "os";
5
- import AdmZip from "adm-zip";
6
- import ignore from "ignore";
7
- import { debug, debugError } from "../lib/debug.js";
8
- import { getBusinessApiBase } from "../lib/api.js";
9
- import { ensureLoggedIn } from "../services/auth.js";
10
- const extractErrorMessage = (body) => {
11
- if (typeof body.error === "string") return body.error;
12
- if (typeof body.message === "string") return body.message;
13
- return void 0;
14
- };
15
- const ALWAYS_EXCLUDE = [".dashnex", ".git"];
16
- const collectFilesToInclude = async (dir, ig) => {
17
- const result = [];
18
- const entries = await fs.readdir(dir, { withFileTypes: true });
19
- for (const entry of entries) {
20
- const relativePath = path.relative(process.cwd(), path.join(dir, entry.name));
21
- const normalized = relativePath.split(path.sep).join("/");
22
- if (ALWAYS_EXCLUDE.some((ex) => normalized === ex || normalized.startsWith(`${ex}/`))) {
23
- continue;
24
- }
25
- if (ig.ignores(normalized)) {
26
- continue;
27
- }
28
- const fullPath = path.join(dir, entry.name);
29
- if (entry.isDirectory()) {
30
- const nested = await collectFilesToInclude(fullPath, ig);
31
- result.push(...nested);
32
- } else if (entry.isFile()) {
33
- result.push(fullPath);
34
- }
35
- }
36
- return result;
37
- };
38
- class PushCommand {
39
- async execute() {
40
- debug("Push flow started");
41
- const session = await ensureLoggedIn();
42
- if (!session) return;
43
- const cwd = process.cwd();
44
- const gitignorePath = path.join(cwd, ".gitignore");
45
- const ig = ignore();
46
- ig.add(ALWAYS_EXCLUDE);
47
- if (await fs.pathExists(gitignorePath)) {
48
- const content = await fs.readFile(gitignorePath, "utf8");
49
- ig.add(content);
50
- }
51
- debug("Building file list");
52
- const files = await collectFilesToInclude(cwd, ig);
53
- debug(`Including ${files.length} files`);
54
- const zip = new AdmZip();
55
- for (const filePath of files) {
56
- const relativePath = path.relative(cwd, filePath);
57
- const entryPath = relativePath.split(path.sep).join("/");
58
- const zipPath = path.dirname(entryPath) === "." ? "" : path.dirname(entryPath);
59
- const zipName = path.basename(entryPath);
60
- zip.addLocalFile(filePath, zipPath, zipName);
61
- }
62
- const tempZipPath = path.join(
63
- os.tmpdir(),
64
- `dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
65
- );
66
- try {
67
- zip.writeZip(tempZipPath);
68
- debug(`Zip written to ${tempZipPath}`);
69
- const url = `${getBusinessApiBase()}/business/v1/cli/push`;
70
- debug(`POST ${url}`);
71
- const formData = new FormData();
72
- formData.append("file", new Blob([await fs.readFile(tempZipPath)]), "archive.zip");
73
- const response = await fetch(url, {
74
- method: "POST",
75
- headers: { Authorization: `Bearer ${session.token}` },
76
- body: formData
77
- });
78
- debug(`Response: ${response.status}`);
79
- if (!response.ok) {
80
- const contentType = response.headers.get("content-type") ?? "";
81
- let message = "Failed to push application.";
82
- if (response.status === 401 || response.status === 403) {
83
- message = "Please run 'npx dashnex login' to authenticate.";
84
- } else if (contentType.includes("application/json")) {
85
- const body = await response.json().catch(() => ({}));
86
- const apiMessage = extractErrorMessage(body);
87
- if (apiMessage) message = apiMessage;
88
- }
89
- console.error(chalk.red(message));
90
- process.exit(1);
91
- }
92
- console.log(chalk.green("Application pushed successfully."));
93
- } catch (error) {
94
- debugError(error);
95
- if (error instanceof Error && error.message.startsWith("EXIT:")) throw error;
96
- console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
97
- process.exit(1);
98
- } finally {
99
- await fs.remove(tempZipPath).catch(() => {
100
- });
101
- debug("Temp zip removed");
102
- }
103
- }
104
- }
105
- export {
106
- PushCommand
107
- };
108
- //# sourceMappingURL=push.js.map
1
+ import e from"chalk";import t from"fs-extra";import o from"path";import i from"os";import s from"adm-zip";import r from"ignore";import n from"inquirer";import{debug as a,debugError as p}from"../lib/debug.js";import{getBusinessApiBase as c}from"../lib/api.js";import{ensureLoggedIn as l}from"../services/auth.js";import{createSpinner as d}from"../lib/spinner.js";import{isUserInterrupt as m,INTERRUPTED_MESSAGE as f}from"../lib/errors.js";import{DeployCommand as h}from"./deploy.js";const u=[".dashnex",".git"],w=async(e,i)=>{const s=[],r=await t.readdir(e,{withFileTypes:!0});for(const t of r){const r=o.relative(process.cwd(),o.join(e,t.name)).split(o.sep).join("/");if(u.some(e=>r===e||r.startsWith(`${e}/`)))continue;if(i.ignores(r))continue;const n=o.join(e,t.name);if(t.isDirectory()){const e=await w(n,i);s.push(...e)}else t.isFile()&&s.push(n)}return s};class g{async execute(){a("Push flow started");const g=await l();if(!g)return;const y=process.cwd(),j=o.join(y,".gitignore"),x=r();if(x.add(u),await t.pathExists(j)){const e=await t.readFile(j,"utf8");x.add(e)}a("Building file list");const $=await w(y,x);a(`Including ${$.length} files`);const b=new s;for(const e of $){const t=o.relative(y,e).split(o.sep).join("/"),i="."===o.dirname(t)?"":o.dirname(t),s=o.basename(t);b.addLocalFile(e,i,s)}const v=o.join(i.tmpdir(),`dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`),F=d();try{F.start("Packaging application..."),b.writeZip(v),a(`Zip written to ${v}`);const o=`${c()}/business/v1/cli/push`;a(`POST ${o}`);const i=await t.readFile(v),s=new Blob([i],{type:"application/zip"}),r=new FormData;r.append("file",s,"archive.zip");const p=new Headers;p.set("Authorization",`Bearer ${g.token}`),F.text("Uploading...");const l=await fetch(o,{method:"POST",headers:p,body:r});if(F.stop(),a(`Response: ${l.status}`),!l.ok){const t=l.headers.get("content-type")??"";let o="Failed to push application.";if(401===l.status||403===l.status)o="Please run 'npx dashnex login' to authenticate.";else if(t.includes("application/json")){const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(await l.json().catch(()=>({})));e&&(o=e)}console.error(e.red(o)),process.exit(1)}console.log(e.green("Application pushed successfully."));const{deploy:d}=await n.prompt([{type:"confirm",name:"deploy",message:"Deploy the application?",default:!0}]);if(a(`User chose to deploy: ${d}`),d){a("Running deploy flow");const e=new h;await e.execute()}}catch(z){if(F.stop(),p(z),z instanceof Error&&z.message.startsWith("EXIT:"))throw z;if(m(z))return void console.log(e.yellow(f));console.error(e.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}finally{await t.remove(v).catch(()=>{}),a("Temp zip removed")}}}export{g as PushCommand};
@@ -1,10 +1 @@
1
- class VersionCommand {
2
- async execute(options) {
3
- const packageJson = await import("../package.json.js");
4
- console.log(packageJson.default.version);
5
- }
6
- }
7
- export {
8
- VersionCommand as default
9
- };
10
- //# sourceMappingURL=version.js.map
1
+ class a{async execute(a){const e=await import("../package.json.js");console.log(e.default.version)}}export{a as default};
@@ -1,52 +1 @@
1
- import chalk from "chalk";
2
- import { debug, debugError } from "../lib/debug.js";
3
- import { getApiBase } from "../lib/api.js";
4
- import { ensureLoggedIn } from "../services/auth.js";
5
- const extractErrorMessage = (body) => {
6
- if (typeof body.error === "string") return body.error;
7
- if (typeof body.message === "string") return body.message;
8
- return void 0;
9
- };
10
- class WhoamiCommand {
11
- async execute() {
12
- debug("Whoami flow started");
13
- const session = await ensureLoggedIn();
14
- if (!session) return;
15
- const url = `${getApiBase()}/users/v1/`;
16
- debug(`GET ${url}`);
17
- try {
18
- const response = await fetch(url, {
19
- headers: { Authorization: `Bearer ${session.token}` }
20
- });
21
- debug(`Response: ${response.status}`);
22
- if (!response.ok) {
23
- const contentType = response.headers.get("content-type") ?? "";
24
- let message = "Failed to fetch user info.";
25
- if (response.status === 401 || response.status === 403) {
26
- message = "Please run 'npx dashnex login' to authenticate.";
27
- } else if (contentType.includes("application/json")) {
28
- const body = await response.json().catch(() => ({}));
29
- const apiMessage = extractErrorMessage(body);
30
- if (apiMessage) message = apiMessage;
31
- }
32
- console.error(chalk.red(message));
33
- process.exit(1);
34
- }
35
- const data = await response.json().catch(() => ({}));
36
- const businessName = typeof data.name === "string" ? data.name : typeof data.companyName === "string" ? data.companyName : "Unknown";
37
- const memberUser = data.memberUser;
38
- const userName = memberUser && typeof memberUser.fullName === "string" ? memberUser.fullName : memberUser && typeof memberUser.name === "string" ? memberUser.name : "Unknown";
39
- debug(`Whoami: ${businessName} (${userName})`);
40
- console.log(chalk.green(`You are logged in as ${businessName} (${userName})`));
41
- } catch (error) {
42
- debugError(error);
43
- if (error instanceof Error && error.message.startsWith("EXIT:")) throw error;
44
- console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
45
- process.exit(1);
46
- }
47
- }
48
- }
49
- export {
50
- WhoamiCommand
51
- };
52
- //# sourceMappingURL=whoami.js.map
1
+ import e from"chalk";import{debug as o,debugError as t}from"../lib/debug.js";import{getApiBase as s}from"../lib/api.js";import{ensureLoggedIn as r}from"../services/auth.js";import{createSpinner as n}from"../lib/spinner.js";import{isUserInterrupt as a,INTERRUPTED_MESSAGE as i}from"../lib/errors.js";class c{async execute(){o("Whoami flow started");const c=await r();if(!c)return;const m=`${s()}/users/v1/`;o(`GET ${m}`);const l=n();l.start("Fetching user info...");try{const t=await fetch(m,{headers:{Authorization:`Bearer ${c.token}`}});if(l.stop(),o(`Response: ${t.status}`),!t.ok){const o=t.headers.get("content-type")??"";let s="Failed to fetch user info.";if(401===t.status||403===t.status)s="Please run 'npx dashnex login' to authenticate.";else if(o.includes("application/json")){const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(await t.json().catch(()=>({})));e&&(s=e)}console.error(e.red(s)),process.exit(1)}const s=await t.json().catch(()=>({})),r="string"==typeof s.name?s.name:"string"==typeof s.companyName?s.companyName:"Unknown",n=s.memberUser,a=n&&"string"==typeof n.fullName?n.fullName:n&&"string"==typeof n.name?n.name:"Unknown";o(`Whoami: ${r} (${a})`),console.log(e.green(`You are logged in as ${r} (${a})`))}catch(f){if(l.stop(),t(f),f instanceof Error&&f.message.startsWith("EXIT:"))throw f;if(a(f))return void console.log(e.yellow(i));console.error(e.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}}export{c as WhoamiCommand};
@@ -1,5 +1 @@
1
- const dashnexConfig = {};
2
- export {
3
- dashnexConfig as default
4
- };
5
- //# sourceMappingURL=dashnex.json.js.map
1
+ const t={};export{t as default};
package/dist/lib/api.js CHANGED
@@ -1,23 +1 @@
1
- const getApiBase = () => {
2
- return process.env.NEXT_PUBLIC_DASHNEX_API_DOMAIN || "https://api.dashnex.com";
3
- };
4
- const getBusinessApiBase = () => {
5
- return process.env.DASHNEX_BUSINESS_API_URL || "https://api.business.dashnex.com";
6
- };
7
- const apiFetch = async (url, options = {}) => {
8
- const response = await fetch(url, {
9
- ...options,
10
- headers: {
11
- "Content-Type": "application/json",
12
- ...options.headers
13
- }
14
- });
15
- const body = await response.json().catch(() => ({}));
16
- return { response, body };
17
- };
18
- export {
19
- apiFetch,
20
- getApiBase,
21
- getBusinessApiBase
22
- };
23
- //# sourceMappingURL=api.js.map
1
+ const s=()=>process.env.NEXT_PUBLIC_DASHNEX_API_DOMAIN||"https://api.dashnex.com",e=()=>process.env.DASHNEX_BUSINESS_API_URL||"https://api.business.dashnex.com",t={qa:"https://3atfynmu2w.us-east-1.awsapprunner.com",live:"https://rkwbzvhcwh.us-east-1.awsapprunner.com"},n=()=>{const s=process.env.APPLICATION_ENV||"live";return t[s]||t.live},a=async(s,e={})=>{const t=await fetch(s,{...e,headers:{"Content-Type":"application/json",...e.headers}}),n=await t.json().catch(()=>({}));return{response:t,body:n}};export{a as apiFetch,s as getApiBase,e as getBusinessApiBase,n as getDeployerBase};
package/dist/lib/debug.js CHANGED
@@ -1,45 +1 @@
1
- import chalk from "chalk";
2
- const isSensitiveKey = (key) => {
3
- const k = key.toLowerCase();
4
- return k.includes("token") || k.includes("password") || k === "auth_code";
5
- };
6
- const sanitizeForLog = (obj) => {
7
- if (obj === null || typeof obj !== "object") return obj;
8
- if (Array.isArray(obj)) {
9
- return obj.map(sanitizeForLog);
10
- }
11
- const result = {};
12
- for (const [key, value] of Object.entries(obj)) {
13
- if (isSensitiveKey(key)) {
14
- result[key] = "[REDACTED]";
15
- } else {
16
- result[key] = sanitizeForLog(value);
17
- }
18
- }
19
- return result;
20
- };
21
- const debug = (msg) => {
22
- if (process.env.DEBUG) {
23
- console.log(chalk.dim(msg));
24
- }
25
- };
26
- const debugJson = (label, body) => {
27
- if (process.env.DEBUG) {
28
- const sanitized = sanitizeForLog(body);
29
- console.log(chalk.dim(`${label}: ${JSON.stringify(sanitized, null, 2)}`));
30
- }
31
- };
32
- const debugError = (error) => {
33
- if (process.env.DEBUG && error instanceof Error) {
34
- console.log(chalk.dim(`Error: ${error.message}`));
35
- if (error.stack) {
36
- console.log(chalk.dim(`Stack: ${error.stack}`));
37
- }
38
- }
39
- };
40
- export {
41
- debug,
42
- debugError,
43
- debugJson
44
- };
45
- //# sourceMappingURL=debug.js.map
1
+ import o from"chalk";const e=o=>{const e=o.toLowerCase();return e.includes("token")||e.includes("password")||"auth_code"===e},r=o=>{if(null===o||"object"!=typeof o)return o;if(Array.isArray(o))return o.map(r);const s={};for(const[n,t]of Object.entries(o))e(n)?s[n]="[REDACTED]":s[n]=r(t);return s},s=e=>{process.env.DEBUG&&console.log(o.dim(e))},n=(e,s)=>{if(process.env.DEBUG){const n=r(s);console.log(o.dim(`${e}: ${JSON.stringify(n,null,2)}`))}},t=e=>{process.env.DEBUG&&e instanceof Error&&(console.log(o.dim(`Error: ${e.message}`)),e.stack&&console.log(o.dim(`Stack: ${e.stack}`)))};export{s as debug,t as debugError,n as debugJson};