edmaxlabs-tools 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edmax
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # EdmaxLabs Tools
2
+
3
+ **EdmaxLabs Tools** is a command-line interface (CLI) for managing EdmaxLabs projects, hosting, domains, and developer services directly from your terminal.
4
+
5
+ It is designed to be **simple**, **fast**, and **automation-friendly**, making it easy to deploy, configure, and manage applications without leaving your workflow.
6
+
7
+ ---
8
+
9
+ ## 🚀 Features
10
+
11
+ - 🔐 Authentication with EdmaxLabs
12
+ - 🌐 Hosting management
13
+ - 📦 Project configuration & deployment
14
+ - ⚙️ Developer-friendly CLI workflows
15
+ - 🧩 Designed for automation and CI/CD
16
+
17
+ ---
18
+
19
+ ## 📦 Installation
20
+
21
+ Install globally using npm:
22
+
23
+ ```bash
24
+ npm install -g edmaxlabs-tools
25
+ ```
26
+
27
+ Verify installation:
28
+
29
+ ```bash
30
+ edmaxlabs --help
31
+ ```
32
+
33
+ ---
34
+
35
+ ## 🔑 Authentication
36
+
37
+ Login to your EdmaxLabs account:
38
+
39
+ ```bash
40
+ edmaxlabs login
41
+ ```
42
+
43
+ This will securely store your access token locally.
44
+
45
+ ---
46
+
47
+ ## 🏗️ Hosting & Domains
48
+
49
+ ### Add a custom domain
50
+
51
+ ```bash
52
+ edmaxlabs hosting --domain
53
+ ```
54
+
55
+ You will be prompted for:
56
+
57
+ - Domain name (e.g. `example.com`)
58
+ - Email (for SSL / verification)
59
+
60
+ The CLI will:
61
+
62
+ - Configure hosting
63
+ - Set up reverse proxy
64
+ - Attempt SSL provisioning automatically
65
+
66
+ > ⚠️ If DNS verification is required, instructions will be provided.
67
+
68
+ ---
69
+
70
+ ## 📁 Project Management
71
+
72
+ Initialize or link a project:
73
+
74
+ ```bash
75
+ edmaxlabs init
76
+ ```
77
+
78
+ Select an existing project or create a new one.
79
+
80
+ ---
81
+
82
+ ## 📌 Requirements
83
+
84
+ - Node.js **v18+**
85
+ - npm **v9+**
86
+
87
+ ---
88
+
89
+ ## 🐞 Issues & Support
90
+
91
+ If you encounter a bug or have a feature request, please open an issue:
92
+
93
+ 👉 [https://github.com/edmaxLabs/edmaxlabs-tools/issues](https://github.com/edmaxLabs/edmaxlabs-tools/issues)
94
+
95
+ ---
96
+
97
+ ## 📜 License
98
+
99
+ MIT License © EdmaxLabs
100
+
101
+ ---
102
+
103
+ ## 🌍 Links
104
+
105
+ - 🌐 Website: [https://edmaxlabs.com](https://edmaxlabs.com)
106
+ - 🐙 GitHub: [https://github.com/edmaxLabs](https://github.com/edmaxLabs)
107
+
108
+ ---
109
+
110
+ ## 🧠 Philosophy
111
+
112
+ > “Build fast. Deploy faster. Stay in control.”
113
+
114
+ EdmaxLabs Tools is built to remove friction between **ideas and production**.
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const init_1 = require("../src/commands/init");
9
+ const login_1 = require("../src/commands/login");
10
+ const hosting_1 = require("../src/commands/hosting");
11
+ const args = process.argv.slice(2);
12
+ async function main() {
13
+ const command = args[0];
14
+ if (args.length === 1) {
15
+ switch (command) {
16
+ case "init":
17
+ await (0, init_1.initCommand)();
18
+ break;
19
+ case "login":
20
+ await (0, login_1.loginCommand)();
21
+ break;
22
+ case "logout":
23
+ await (0, login_1.logoutCommand)();
24
+ break;
25
+ case "deploy":
26
+ await (0, hosting_1.deployCommand)();
27
+ break;
28
+ default:
29
+ console.log(chalk_1.default.green.bold("EdmaxLabs"));
30
+ console.log("Usage: edmaxlabs <command>");
31
+ console.log("Commands:\n init \n login \n logout");
32
+ break;
33
+ }
34
+ }
35
+ else {
36
+ if (args.length > 1) {
37
+ if (command === "hosting" && args[1] === "--setup") {
38
+ (0, hosting_1.hostingSetup)();
39
+ }
40
+ if (command === "hosting" && args[1] === "--domain") {
41
+ (0, hosting_1.hostingDomainSetup)();
42
+ }
43
+ }
44
+ }
45
+ }
46
+ main();
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireAuth = requireAuth;
4
+ const saveLocal_1 = require("../utils/saveLocal");
5
+ function requireAuth() {
6
+ const token = (0, saveLocal_1.getLocal)("token");
7
+ if (!token) {
8
+ console.log("❌ Not logged in. Run: edmaxlabs login");
9
+ process.exit(1);
10
+ }
11
+ return token;
12
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.login = login;
7
+ const open_1 = __importDefault(require("open"));
8
+ const http_1 = __importDefault(require("http"));
9
+ const saveLocal_1 = require("./utils/saveLocal");
10
+ async function login() {
11
+ const port = 53682;
12
+ const redirectUri = `http://localhost:${port}/callback`;
13
+ // 1. Start temporary local server
14
+ console.log("Logging in...");
15
+ const server = http_1.default.createServer((req, res) => {
16
+ if (!req.url)
17
+ return;
18
+ const url = new URL(req.url, redirectUri);
19
+ if (url.pathname === "/callback") {
20
+ const token = url.searchParams.get("token");
21
+ if (!token) {
22
+ res.end("Authentication failed. No token.");
23
+ return;
24
+ }
25
+ // 2. Save token
26
+ (0, saveLocal_1.saveLocal)("token", token);
27
+ res.end("✅ Authentication successful. You can close this window.");
28
+ console.log("✅ Logged in successfully");
29
+ server.close();
30
+ }
31
+ });
32
+ server.listen(port);
33
+ // 3. Open browser
34
+ await (0, open_1.default)(`https://auth.edmaxlabs.com/cli?redirect_uri=${encodeURIComponent(redirectUri)}`);
35
+ // Mock API response: generate a fake userId
36
+ const userId = "user_" + Math.random().toString(36).substring(2, 10);
37
+ return userId;
38
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.deployDatabaseRules = deployDatabaseRules;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const config_1 = require("../config");
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const FormData = require("form-data");
12
+ const node_fetch_1 = __importDefault(require("node-fetch"));
13
+ const constants_1 = require("../constants");
14
+ // Example: deploy function
15
+ async function deployDatabaseRules() {
16
+ const cwd = process.cwd();
17
+ const existingConfig = await (0, config_1.loadConfig)(cwd);
18
+ if (!existingConfig) {
19
+ console.log(chalk_1.default.red("⚠ No project initialized. Run `edmaxlabs init` first."));
20
+ return;
21
+ }
22
+ const rules = `${path_1.default.join(cwd, existingConfig.database.rules)}`;
23
+ if (!fs_extra_1.default.existsSync(rules)) {
24
+ console.log(chalk_1.default.red("DB Rules Not Found at - "), chalk_1.default.underline(rules));
25
+ return;
26
+ }
27
+ const form = new FormData();
28
+ form.append("file", fs_extra_1.default.createReadStream(rules), path_1.default.basename(rules));
29
+ form.append("hostname", existingConfig.hosting.host);
30
+ form.append("project", existingConfig.project.id);
31
+ const response = await (0, node_fetch_1.default)(`https://api.edmaxlabs.com/api/v2/db/rules/compile`, {
32
+ method: "POST",
33
+ headers: {
34
+ authorization: constants_1.CLIENT_BEARER_TOKEN,
35
+ ...form.getHeaders(), // 👈 REQUIRED in Node
36
+ },
37
+ body: form,
38
+ });
39
+ const rr = await response.json();
40
+ const data = rr;
41
+ if (!response.ok) {
42
+ console.log(chalk_1.default.red(`DB Rules Deployment Failed : ${data.error}`));
43
+ }
44
+ else {
45
+ if (data.success) {
46
+ console.log(chalk_1.default.green(`DB Rules Deployed Successful\n`));
47
+ return;
48
+ }
49
+ console.log(chalk_1.default.red(`DB Rules Deployment Failed - ${data.error}`));
50
+ }
51
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.deployFunctions = deployFunctions;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const config_1 = require("../config");
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const FormData = require("form-data");
12
+ const node_fetch_1 = __importDefault(require("node-fetch"));
13
+ const constants_1 = require("../constants");
14
+ // Example: deploy function
15
+ async function deployFunctions() {
16
+ const cwd = process.cwd();
17
+ const existingConfig = await (0, config_1.loadConfig)(cwd);
18
+ if (!existingConfig) {
19
+ console.log(chalk_1.default.red("⚠ No project initialized. Run `edmaxlabs init` first."));
20
+ return;
21
+ }
22
+ const functions = `${path_1.default.join(cwd, existingConfig.functions.file)}`;
23
+ if (!fs_extra_1.default.existsSync(functions)) {
24
+ console.log(chalk_1.default.red("Functions Not Found at - "), chalk_1.default.underline(functions));
25
+ return;
26
+ }
27
+ const form = new FormData();
28
+ form.append("file", fs_extra_1.default.createReadStream(functions), path_1.default.basename(functions));
29
+ form.append("hostname", existingConfig.hosting.host);
30
+ form.append("project", existingConfig.project.id);
31
+ const response = await (0, node_fetch_1.default)(`https://api.edmaxlabs.com/api/v2/functions/compile`, {
32
+ method: "POST",
33
+ headers: {
34
+ authorization: constants_1.CLIENT_BEARER_TOKEN,
35
+ ...form.getHeaders(), // 👈 REQUIRED in Node
36
+ },
37
+ body: form,
38
+ });
39
+ const data = (await response.json());
40
+ if (!response.ok) {
41
+ console.log(chalk_1.default.red(`Functions Deployment Failed : ${data.error}`));
42
+ }
43
+ else {
44
+ if (data.success) {
45
+ console.log(chalk_1.default.green(`Functions Deployed Successful - \n V : ${data.version}`));
46
+ return;
47
+ }
48
+ console.log(chalk_1.default.red(`Functions Deployment Failed - ${data.error}`));
49
+ }
50
+ }
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.deployCommand = deployCommand;
7
+ exports.hostingSetup = hostingSetup;
8
+ exports.hostingDomainSetup = hostingDomainSetup;
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const inquirer_1 = __importDefault(require("inquirer"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const session_1 = require("../auth/session");
13
+ const config_1 = require("../config");
14
+ const fs_extra_1 = __importDefault(require("fs-extra"));
15
+ const FormData = require("form-data");
16
+ const node_fetch_1 = __importDefault(require("node-fetch"));
17
+ const constants_1 = require("../constants");
18
+ const zipFile_1 = require("../utils/zipFile");
19
+ const functions_1 = require("./functions");
20
+ const database_1 = require("./database");
21
+ // Example: deploy function
22
+ async function deployCommand() {
23
+ console.log(chalk_1.default.cyan.bold("\nEdmaxLabs - Deploy\n"));
24
+ // 🔐 Ensure user is logged in
25
+ const token = (0, session_1.requireAuth)();
26
+ // 🔄 Load project config
27
+ const cwd = process.cwd();
28
+ const config = await (0, config_1.loadConfig)(cwd);
29
+ if (!config || !config.project) {
30
+ console.log(chalk_1.default.red("⚠ No project initialized. Run `edmaxlabs init` first."));
31
+ return;
32
+ }
33
+ const projectName = config.project.name;
34
+ // 🔹 Ask which services to deploy (based on initialized project)
35
+ const availableServices = Object.keys(config).filter((k) => ["database", "auth", "storage", "functions", "hosting"].includes(k) &&
36
+ Object.keys(config[k]).length > 0);
37
+ if (availableServices.length === 0) {
38
+ console.log(chalk_1.default.gray("No services configured to deploy."));
39
+ return;
40
+ }
41
+ const { servicesToDeploy } = await inquirer_1.default.prompt([
42
+ {
43
+ type: "checkbox",
44
+ name: "servicesToDeploy",
45
+ message: `Select services to deploy for project "${projectName}":`,
46
+ choices: availableServices.map((s) => ({ name: s, value: s })),
47
+ },
48
+ ]);
49
+ if (servicesToDeploy.length === 0) {
50
+ console.log(chalk_1.default.gray("✅ Nothing selected. Aborting deploy."));
51
+ return;
52
+ }
53
+ console.log(chalk_1.default.blue(`\n🚀 Deploying project "${projectName}"...`));
54
+ // 🔹 Loop over selected services
55
+ for (const service of servicesToDeploy) {
56
+ switch (service) {
57
+ case "hosting":
58
+ console.log(chalk_1.default.green(`🔹 Hosting directory: ${config.hosting.dir}`));
59
+ await deployHosting(config, cwd);
60
+ break;
61
+ case "functions":
62
+ console.log(chalk_1.default.green(`🔹 Functions : ${config.functions.file}`));
63
+ await (0, functions_1.deployFunctions)();
64
+ break;
65
+ case "database":
66
+ console.log(chalk_1.default.green(`🔹 Database Rules : ${config.database.rules}`));
67
+ await (0, database_1.deployDatabaseRules)();
68
+ break;
69
+ case "storage":
70
+ console.log(chalk_1.default.green(`🔹 Storage Rules: ${config.storage.rules}`));
71
+ // TODO: trigger storage deploy
72
+ break;
73
+ case "auth":
74
+ console.log(chalk_1.default.green(`🔹 Deploying Auth service...`));
75
+ // TODO: trigger auth deploy
76
+ break;
77
+ default:
78
+ console.log(chalk_1.default.yellow(`⚠ Unknown service: ${service}`));
79
+ }
80
+ }
81
+ /**
82
+ * Hosting deployment blueprint
83
+ */
84
+ async function deployHosting(config, cwd) {
85
+ const dir = config.hosting.dir || "public";
86
+ const deployPath = path_1.default.join(cwd, dir);
87
+ if (!(await fs_extra_1.default.pathExists(deployPath))) {
88
+ console.log(chalk_1.default.red(`❌ Hosting directory "${dir}" not found`));
89
+ return;
90
+ }
91
+ const zipPath = path_1.default.join(cwd, `${dir}.zip`);
92
+ console.log(chalk_1.default.green(`🔹 Deploying : ${zipPath}`));
93
+ const zpres = await (0, zipFile_1.zipDir)(deployPath, zipPath);
94
+ const finalPath = zipPath;
95
+ console.log("ZIP | ", zpres);
96
+ const form = new FormData();
97
+ form.append("file", // 👈 MUST match upload.single("file")
98
+ fs_extra_1.default.createReadStream(finalPath), path_1.default.basename(finalPath));
99
+ form.append("hostname", config.hosting.host);
100
+ form.append("project", config.project.id);
101
+ // This is an example endpoint; adjust to your actual EdmaxLabs hosting API
102
+ const response = await (0, node_fetch_1.default)(`https://api.edmaxlabs.com/api/v2/hosting/upload`, {
103
+ method: "POST",
104
+ headers: {
105
+ authorization: constants_1.CLIENT_BEARER_TOKEN,
106
+ ...form.getHeaders(), // 👈 REQUIRED in Node
107
+ },
108
+ body: form,
109
+ });
110
+ const data = (await response.json());
111
+ if (!response.ok) {
112
+ console.log(chalk_1.default.red(`Hosting Aborted : ${data.error}`));
113
+ }
114
+ else {
115
+ console.log(chalk_1.default.green(`Hosted Successful - `), chalk_1.default.underline(`https://${data.url}`));
116
+ }
117
+ console.log(chalk_1.default.green("✅ Hosting deployed successfully"));
118
+ }
119
+ }
120
+ async function hostingSetup() {
121
+ const cwd = process.cwd();
122
+ const existingConfig = await (0, config_1.loadConfig)(cwd);
123
+ if (!existingConfig) {
124
+ console.log(chalk_1.default.red("⚠ No project initialized. Run `edmaxlabs init` first."));
125
+ return;
126
+ }
127
+ const defaultHostname = `${existingConfig.project.name.replaceAll(" ", "-").toLowerCase()}.edmaxlabs.com`;
128
+ const { host = defaultHostname } = await inquirer_1.default.prompt([
129
+ {
130
+ type: "input",
131
+ name: "host",
132
+ message: "Hosting : HostName :",
133
+ default: defaultHostname,
134
+ },
135
+ ]);
136
+ const payload = {
137
+ ...existingConfig,
138
+ hosting: {
139
+ ...existingConfig.hosting,
140
+ host,
141
+ },
142
+ };
143
+ const bjson = JSON.stringify({
144
+ hostname: host,
145
+ project: existingConfig.project.id,
146
+ });
147
+ console.log(bjson);
148
+ const response = await (0, node_fetch_1.default)(`https://api.edmaxlabs.com/api/v2/hosting/register`, {
149
+ method: "POST",
150
+ headers: {
151
+ "Content-Type": "application/json",
152
+ authorization: constants_1.CLIENT_BEARER_TOKEN,
153
+ },
154
+ body: bjson,
155
+ });
156
+ const data = (await response.json());
157
+ if (!response.ok) {
158
+ console.log(chalk_1.default.red(`Hosting Setup Failed : ${data.error}`));
159
+ }
160
+ else {
161
+ if (data.success) {
162
+ await (0, config_1.saveConfig)(cwd, payload);
163
+ console.log(chalk_1.default.green(`Hosting Setup Successful - `), chalk_1.default.underline(data.url));
164
+ return;
165
+ }
166
+ console.log(chalk_1.default.red(`Hosting Setup Failed - Try Again.`));
167
+ }
168
+ }
169
+ async function hostingDomainSetup() {
170
+ const cwd = process.cwd();
171
+ const existingConfig = await (0, config_1.loadConfig)(cwd);
172
+ if (!existingConfig) {
173
+ console.log(chalk_1.default.red("⚠ No project initialized. Run `edmaxlabs init` first."));
174
+ return;
175
+ }
176
+ const { domain } = await inquirer_1.default.prompt([
177
+ {
178
+ type: "input",
179
+ name: "domain",
180
+ message: "Hosting : Domain (e.g; example.com) :",
181
+ },
182
+ ]);
183
+ const { email } = await inquirer_1.default.prompt([
184
+ {
185
+ type: "input",
186
+ name: "email",
187
+ message: "Hosting : 2FA (e.g; me@example.com) :",
188
+ },
189
+ ]);
190
+ const payload = {
191
+ email,
192
+ domain,
193
+ project: existingConfig.project.id,
194
+ };
195
+ const bjson = JSON.stringify(payload);
196
+ console.log(bjson);
197
+ const response = await (0, node_fetch_1.default)(`https://api.edmaxlabs.com/api/v2/hosting/domain/bind`, {
198
+ method: "POST",
199
+ headers: {
200
+ "Content-Type": "application/json",
201
+ authorization: constants_1.CLIENT_BEARER_TOKEN,
202
+ },
203
+ body: bjson,
204
+ });
205
+ const d = await response.json();
206
+ console.log(d);
207
+ const data = d;
208
+ if (!response.ok) {
209
+ console.log(chalk_1.default.red(`Domain Setup Failed : ${data.error}`));
210
+ }
211
+ else {
212
+ if (data.success) {
213
+ await (0, config_1.saveConfig)(cwd, payload);
214
+ console.log(chalk_1.default.green(data.message));
215
+ return;
216
+ }
217
+ console.log(chalk_1.default.red(`Hosting Setup Failed - Try Again.`));
218
+ }
219
+ }
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.initCommand = initCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const session_1 = require("../auth/session");
11
+ const config_1 = require("../config");
12
+ const projects_1 = require("./projects");
13
+ async function initCommand() {
14
+ console.log(chalk_1.default.cyan.bold("\nEdmaxLabs\n"));
15
+ const { confirm } = await inquirer_1.default.prompt([
16
+ {
17
+ type: "confirm",
18
+ name: "confirm",
19
+ message: "Do you wish to continue?",
20
+ default: true,
21
+ },
22
+ ]);
23
+ if (!confirm) {
24
+ console.log("❌ Initialization cancelled");
25
+ return;
26
+ }
27
+ // 🔐 Ensure user is logged in
28
+ const token = (0, session_1.requireAuth)();
29
+ //Current Path Provider
30
+ const cwd = process.cwd();
31
+ // Load existing local config (if re-init)
32
+ const existingConfig = await (0, config_1.loadConfig)(cwd);
33
+ // TODO: later → fetch projects using token
34
+ const projects = (await (0, projects_1.loadProjects)()) ?? [];
35
+ if (projects.length === 0) {
36
+ console.log(chalk_1.default.gray(`No Projects Found \n Visit - `), chalk_1.default.underline(`https://console.edmaxlabs.com/projects \n`), chalk_1.default.gray(`To get Started.`));
37
+ return;
38
+ }
39
+ const { project } = await inquirer_1.default.prompt([
40
+ {
41
+ type: "checkbox", // ⬅ arrow keys
42
+ name: "project",
43
+ message: "Select a project (Single Selection) > ",
44
+ choices: [
45
+ ...projects.map(({ name, _id }) => {
46
+ return {
47
+ id: _id,
48
+ name: name,
49
+ value: {
50
+ id: _id,
51
+ name,
52
+ },
53
+ };
54
+ }),
55
+ ],
56
+ },
57
+ ]);
58
+ if (project.length > 1) {
59
+ console.log(chalk_1.default.gray(`Select only 1 Project`));
60
+ return;
61
+ }
62
+ if (project.length === 0) {
63
+ console.log(chalk_1.default.red(`Invalid Project.`));
64
+ return;
65
+ }
66
+ // 🔜 Multiple service selection
67
+ const { services } = await inquirer_1.default.prompt([
68
+ {
69
+ type: "checkbox",
70
+ name: "services",
71
+ message: "Select services :",
72
+ choices: [
73
+ { name: "Database", value: "database" },
74
+ { name: "Auth", value: "auth" },
75
+ { name: "Storage", value: "storage" },
76
+ { name: "Functions", value: "functions" },
77
+ { name: "Hosting", value: "hosting" },
78
+ ],
79
+ },
80
+ ]);
81
+ const pjc = project[0];
82
+ let payload = {
83
+ project: pjc,
84
+ services,
85
+ hosting: {},
86
+ functions: {},
87
+ database: {},
88
+ storage: {},
89
+ };
90
+ if (services.includes("hosting")) {
91
+ const { hosting = "public" } = await inquirer_1.default.prompt([
92
+ {
93
+ type: "input",
94
+ name: "hosting",
95
+ message: "Hosting : Hosting Directory :",
96
+ default: "public",
97
+ },
98
+ ]);
99
+ const defaultHostname = `${pjc.project.name.replaceAll(" ", "-").toLowerCase()}`;
100
+ const { host = defaultHostname } = await inquirer_1.default.prompt([
101
+ {
102
+ type: "input",
103
+ name: "host",
104
+ message: "Hosting : HostName :",
105
+ default: defaultHostname,
106
+ },
107
+ ]);
108
+ payload = {
109
+ ...payload,
110
+ hosting: {
111
+ dir: hosting,
112
+ host,
113
+ },
114
+ };
115
+ }
116
+ if (services.includes("functions")) {
117
+ const { functions = "efunctions.ts" } = await inquirer_1.default.prompt([
118
+ {
119
+ type: "input",
120
+ name: "functions",
121
+ message: "Functions : Path : ",
122
+ default: "efunctions.ts",
123
+ },
124
+ ]);
125
+ payload = {
126
+ ...payload,
127
+ functions: {
128
+ file: functions,
129
+ },
130
+ };
131
+ }
132
+ if (services.includes("database")) {
133
+ const { rule = "edatabase.rules.json" } = await inquirer_1.default.prompt([
134
+ {
135
+ type: "input",
136
+ name: "rule",
137
+ message: "Database : Rules : ",
138
+ default: "edatabase.rules.json",
139
+ },
140
+ ]);
141
+ payload = {
142
+ ...payload,
143
+ database: {
144
+ rules: rule,
145
+ },
146
+ };
147
+ }
148
+ if (services.includes("storage")) {
149
+ const { rule = "estorage.rules.json" } = await inquirer_1.default.prompt([
150
+ {
151
+ type: "input",
152
+ name: "rule",
153
+ message: "Storage : Rules : ",
154
+ default: "estorage.rules.json",
155
+ },
156
+ ]);
157
+ payload = {
158
+ ...payload,
159
+ storage: {
160
+ rules: rule,
161
+ },
162
+ };
163
+ }
164
+ await (0, config_1.saveConfig)(cwd, payload);
165
+ console.log(chalk_1.default.green(`\n✅ Project initialized at ${path_1.default.join(cwd, ".edmaxlabs")}`));
166
+ }
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loginCommand = loginCommand;
7
+ exports.logoutCommand = logoutCommand;
8
+ const open_1 = __importDefault(require("open"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const node_fetch_1 = __importDefault(require("node-fetch")); // or global fetch in Node 18+
11
+ const saveLocal_1 = require("../utils/saveLocal");
12
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
13
+ const constants_1 = require("../constants");
14
+ const POLL_INTERVAL = 2000; // 2 seconds
15
+ async function loginCommand() {
16
+ try {
17
+ //PREACTIONS
18
+ //#1 Load the saved token
19
+ const savedToken = (0, saveLocal_1.getLocal)("token");
20
+ if (savedToken) {
21
+ const decoded = jsonwebtoken_1.default.decode(savedToken);
22
+ console.log(chalk_1.default.cyan("Already Logged in as - "), chalk_1.default.underline(decoded.email ?? ""));
23
+ return;
24
+ }
25
+ // 1️⃣ Create CLI session
26
+ const createRes = await (0, node_fetch_1.default)("https://api.edmaxlabs.com/api/v2/cli/create/session", {
27
+ method: "POST",
28
+ headers: {
29
+ "Content-Type": "application/json",
30
+ authorization: constants_1.AUTH_BEARER_TOKEN,
31
+ },
32
+ body: JSON.stringify({}),
33
+ });
34
+ const cres = await createRes.json();
35
+ const { session: sessionId } = cres;
36
+ if (!sessionId)
37
+ throw new Error("Failed to create CLI session");
38
+ // 2️⃣ Open browser for frontend login
39
+ const frontendLoginUrl = `https://auth.edmaxlabs.com/cli/login?session=${sessionId}`;
40
+ console.log(chalk_1.default.cyan("🌐 Opening browser for authentication...\n"), chalk_1.default.underline(frontendLoginUrl));
41
+ await (0, open_1.default)(frontendLoginUrl);
42
+ // 3️⃣ Poll for token
43
+ let token = null;
44
+ while (!token) {
45
+ const pollRes = await (0, node_fetch_1.default)("https://api.edmaxlabs.com/api/v2/cli/pull/session", {
46
+ method: "POST",
47
+ headers: {
48
+ "Content-Type": "application/json",
49
+ authorization: constants_1.AUTH_BEARER_TOKEN,
50
+ },
51
+ body: JSON.stringify({ sessionId }),
52
+ });
53
+ const data = (await pollRes.json());
54
+ if (data.success && data.token) {
55
+ token = data.token;
56
+ break;
57
+ }
58
+ // Wait before next poll
59
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
60
+ }
61
+ // 4️⃣ Save token locally
62
+ (0, saveLocal_1.saveLocal)("token", token);
63
+ console.log(chalk_1.default.green("✅ Logged in successfully!"));
64
+ }
65
+ catch (err) {
66
+ console.error(chalk_1.default.red("❌ CLI login failed:"), err.message || err);
67
+ }
68
+ }
69
+ async function logoutCommand() {
70
+ try {
71
+ const savedToken = (0, saveLocal_1.getLocal)("token");
72
+ if (savedToken) {
73
+ const decoded = jsonwebtoken_1.default.decode(savedToken);
74
+ (0, saveLocal_1.saveLocal)("token", "");
75
+ console.log(chalk_1.default.cyan("Logged out as - "), chalk_1.default.underline(decoded.email ?? ""));
76
+ return;
77
+ }
78
+ }
79
+ catch (err) {
80
+ console.error(chalk_1.default.red("❌ CLI logout failed:"), err.message || err);
81
+ }
82
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadProjects = loadProjects;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const node_fetch_1 = __importDefault(require("node-fetch")); // or global fetch in Node 18+
9
+ const saveLocal_1 = require("../utils/saveLocal");
10
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
11
+ const constants_1 = require("../constants");
12
+ const POLL_INTERVAL = 2000; // 2 seconds
13
+ async function loadProjects() {
14
+ try {
15
+ //#1 Load the saved token
16
+ const savedToken = (0, saveLocal_1.getLocal)("token");
17
+ if (!savedToken) {
18
+ return [];
19
+ }
20
+ const decoded = jsonwebtoken_1.default.decode(savedToken);
21
+ const projectsRes = await (0, node_fetch_1.default)("https://api.edmaxlabs.com/api/v2/projects", {
22
+ method: "POST",
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ authorization: constants_1.CLIENT_BEARER_TOKEN,
26
+ },
27
+ body: JSON.stringify({
28
+ uid: decoded.uid,
29
+ }),
30
+ });
31
+ if (!projectsRes.ok) {
32
+ throw new Error(`Failed to load Projects \n\n ${projectsRes.status ?? ""}`);
33
+ }
34
+ const { success, projects, error } = (await projectsRes.json());
35
+ if (!projectsRes.ok) {
36
+ throw new Error(error ?? "Failed to load Projects");
37
+ }
38
+ if (!success)
39
+ throw new Error("Failed to load Projects");
40
+ return projects;
41
+ }
42
+ catch (err) {
43
+ console.error(chalk_1.default.red("❌ Loading Projects failed:"), err.message || err);
44
+ }
45
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.saveConfig = saveConfig;
7
+ exports.loadConfig = loadConfig;
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const CONFIG_FOLDER = ".edmaxlabs";
11
+ const CONFIG_FILE = "config.json";
12
+ async function saveConfig(folder, data) {
13
+ const configPath = path_1.default.join(folder, CONFIG_FOLDER);
14
+ await fs_extra_1.default.ensureDir(configPath);
15
+ await fs_extra_1.default.writeJson(path_1.default.join(configPath, CONFIG_FILE), data, { spaces: 2 });
16
+ }
17
+ async function loadConfig(folder) {
18
+ const file = path_1.default.join(folder, CONFIG_FOLDER, CONFIG_FILE);
19
+ if (await fs_extra_1.default.pathExists(file)) {
20
+ return fs_extra_1.default.readJson(file);
21
+ }
22
+ return null;
23
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CLIENT_BEARER_TOKEN = exports.AUTH_BEARER_TOKEN = void 0;
4
+ exports.AUTH_BEARER_TOKEN = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2OTIyZTI0Yjk2NTE1NWY2YTlhNGQ5ZmUiLCJyb2xlIjoiY2xpZW50IiwicHJvamVjdElkIjoiNjkyMmQ1OTgyYjIzYWU2YTEyZTUxZDg1IiwiaWF0IjoxNzcwMjgzNjI5fQ.Lbm2YYNlJ51XqAErKbezTfYDnK7wewWABCa39hMQQEY";
5
+ exports.CLIENT_BEARER_TOKEN = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2OTIyZTI0Yjk2NTE1NWY2YTlhNGQ5ZmUiLCJyb2xlIjoiY2xpZW50IiwicHJvamVjdElkIjoiNjk4NDU3YzJmNzQ0ODU1MGI5ZTE2NmM0IiwiaWF0IjoxNzcwMjgyNzc2fQ.X5lOY68k_i1rNz4a9WANidnweePfOcmqTWkRjBzLW7A";
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.selectProject = selectProject;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ async function selectProject(projects) {
9
+ const { projectId } = await inquirer_1.default.prompt([
10
+ {
11
+ type: "rawlist", // ⬅ arrow keys
12
+ name: "projectId",
13
+ message: "Select a project:",
14
+ choices: [
15
+ ...projects.map((p) => ({
16
+ name: p.name,
17
+ value: p.id,
18
+ })),
19
+ { name: "➕ Create new project", value: "__new__" },
20
+ ],
21
+ },
22
+ ]);
23
+ return projectId;
24
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.saveLocal = saveLocal;
7
+ exports.getLocal = getLocal;
8
+ const configstore_1 = __importDefault(require("configstore"));
9
+ const config = new configstore_1.default("edmaxlabs");
10
+ function saveLocal(key, value) {
11
+ config.set(key, value);
12
+ }
13
+ function getLocal(key) {
14
+ return config.get(key);
15
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.zipDir = zipDir;
7
+ const archiver_1 = __importDefault(require("archiver"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra")); // fs-extra for convenience
9
+ const path_1 = __importDefault(require("path"));
10
+ async function zipDir(source, out) {
11
+ // Ensure the parent folder exists
12
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(out));
13
+ const archive = (0, archiver_1.default)("zip", { zlib: { level: 9 } });
14
+ const stream = fs_extra_1.default.createWriteStream(out);
15
+ return new Promise((resolve, reject) => {
16
+ archive
17
+ .directory(source, false)
18
+ .on("error", (err) => reject(err))
19
+ .pipe(stream);
20
+ stream.on("close", () => resolve(out));
21
+ stream.on("error", (err) => reject(err));
22
+ archive.finalize();
23
+ });
24
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "edmaxlabs-tools",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "edmaxlabs": "dist/bin/edmaxlabs-tools.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "start": "ts-node bin/edmaxlabs-tools.ts",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/edmaxLabs/edmaxlabs-tools.git"
20
+ },
21
+ "keywords": [],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "type": "commonjs",
25
+ "bugs": {
26
+ "url": "https://github.com/edmaxLabs/edmaxlabs-tools/issues"
27
+ },
28
+ "homepage": "https://github.com/edmaxLabs/edmaxlabs-tools#readme",
29
+ "devDependencies": {
30
+ "@types/archiver": "^7.0.0",
31
+ "@types/form-data": "^2.2.1",
32
+ "@types/fs-extra": "^11.0.4",
33
+ "@types/jsonwebtoken": "^9.0.10",
34
+ "@types/node": "^25.2.0",
35
+ "ts-node": "^10.9.2",
36
+ "typescript": "^5.9.3"
37
+ },
38
+ "dependencies": {
39
+ "archiver": "^7.0.1",
40
+ "chalk": "^5.6.2",
41
+ "configstore": "^8.0.0",
42
+ "form-data": "^4.0.5",
43
+ "fs-extra": "^11.3.3",
44
+ "inquirer": "^13.2.2",
45
+ "jsonwebtoken": "^9.0.3",
46
+ "node-fetch": "^3.3.2",
47
+ "open": "^11.0.0"
48
+ }
49
+ }