@envsafes-org/cli 0.0.6

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.
@@ -0,0 +1,55 @@
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 chalk_1 = __importDefault(require("chalk"));
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const index_js_1 = require("../index.js");
10
+ const api_js_1 = require("../utils/api.js");
11
+ const open_1 = __importDefault(require("open"));
12
+ async function login(options) {
13
+ let token = options.token;
14
+ if (!token) {
15
+ const dashboardUrl = index_js_1.config.get("dashboardUrl");
16
+ const loginUrl = `${dashboardUrl}/dashboard?redirect=tokens`;
17
+ console.log(chalk_1.default.cyan(`\nOuverture du navigateur pour générer un token...\n`));
18
+ console.log(chalk_1.default.dim(`Si le navigateur ne s'ouvre pas, visitez : ${loginUrl}`));
19
+ try {
20
+ await (0, open_1.default)(loginUrl);
21
+ }
22
+ catch (err) {
23
+ // Ignore error if browser fails to open
24
+ }
25
+ const answers = await inquirer_1.default.prompt([
26
+ {
27
+ type: "password",
28
+ name: "token",
29
+ message: "Collez votre token API EnvSafe ici:",
30
+ mask: "*",
31
+ validate: (input) => input.length > 0 || "Le token est requis",
32
+ },
33
+ ]);
34
+ token = answers.token;
35
+ }
36
+ console.log(chalk_1.default.dim("Vérification du token..."));
37
+ try {
38
+ // Test the token
39
+ const response = await (0, api_js_1.apiRequest)("/api/v1/projects", {
40
+ method: "GET",
41
+ token,
42
+ });
43
+ if (response.error) {
44
+ console.log(chalk_1.default.red(`✗ Erreur: ${response.error}`));
45
+ return;
46
+ }
47
+ // Save token
48
+ index_js_1.config.set("token", token);
49
+ console.log(chalk_1.default.green("✓ Connexion réussie!"));
50
+ console.log(chalk_1.default.dim(` ${response.count} projet(s) accessible(s)`));
51
+ }
52
+ catch (error) {
53
+ console.log(chalk_1.default.red(`✗ Erreur de connexion: ${error.message}`));
54
+ }
55
+ }
@@ -0,0 +1 @@
1
+ export declare function projects(): Promise<void>;
@@ -0,0 +1,40 @@
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.projects = projects;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const index_js_1 = require("../index.js");
9
+ const api_js_1 = require("../utils/api.js");
10
+ async function projects() {
11
+ const token = index_js_1.config.get("token");
12
+ if (!token) {
13
+ console.log(chalk_1.default.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
14
+ return;
15
+ }
16
+ try {
17
+ const response = await (0, api_js_1.apiRequest)("/api/v1/projects", {
18
+ method: "GET",
19
+ token,
20
+ });
21
+ if (response.error) {
22
+ console.log(chalk_1.default.red(`✗ ${response.error}`));
23
+ return;
24
+ }
25
+ console.log(chalk_1.default.cyan("\n📁 Projets accessibles\n"));
26
+ if (response.projects.length === 0) {
27
+ console.log(chalk_1.default.dim(" Aucun projet trouvé."));
28
+ return;
29
+ }
30
+ for (const project of response.projects) {
31
+ console.log(chalk_1.default.bold(` ${project.name}`));
32
+ console.log(chalk_1.default.dim(` slug: ${project.slug}`));
33
+ console.log(chalk_1.default.dim(` envs: ${project.environments.join(", ")}`));
34
+ console.log("");
35
+ }
36
+ }
37
+ catch (error) {
38
+ console.log(chalk_1.default.red(`✗ Erreur: ${error.message}`));
39
+ }
40
+ }
@@ -0,0 +1,4 @@
1
+ export declare function pull(project: string, options: {
2
+ env: string;
3
+ output: string;
4
+ }): Promise<void>;
@@ -0,0 +1,46 @@
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.pull = pull;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const ora_1 = __importDefault(require("ora"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const index_js_1 = require("../index.js");
11
+ const api_js_1 = require("../utils/api.js");
12
+ async function pull(project, options) {
13
+ const token = index_js_1.config.get("token");
14
+ if (!token) {
15
+ console.log(chalk_1.default.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
16
+ return;
17
+ }
18
+ const spinner = (0, ora_1.default)(`Téléchargement des variables pour ${project} (${options.env})...`).start();
19
+ try {
20
+ const response = await (0, api_js_1.apiRequest)(`/api/v1/projects/${project}/${options.env}`, {
21
+ method: "GET",
22
+ token,
23
+ });
24
+ if (response.error) {
25
+ spinner.fail(chalk_1.default.red(response.error));
26
+ // Suggestion intelligente si l'utilisateur a confondu projet et environnement
27
+ const commonEnvs = ["development", "dev", "staging", "stg", "production", "prod", "test"];
28
+ if (commonEnvs.includes(project.toLowerCase()) || response.error.includes("trouvé")) {
29
+ console.log(chalk_1.default.yellow("\n💡 Astuce : La syntaxe est 'envsafe pull <MON_PROJET>'"));
30
+ console.log(chalk_1.default.yellow(` Vous avez essayé de pull le projet "${project}".`));
31
+ console.log(chalk_1.default.yellow(` Utilisez 'envsafe projects' pour voir la liste de vos projets/slugs.`));
32
+ }
33
+ return;
34
+ }
35
+ // Format as .env content
36
+ const envContent = Object.entries(response.variables)
37
+ .map(([key, value]) => `${key}="${value}"`)
38
+ .join("\n");
39
+ // Write to file
40
+ fs_1.default.writeFileSync(options.output, envContent + "\n");
41
+ spinner.succeed(chalk_1.default.green(`✓ ${response.count} variable(s) téléchargée(s) vers ${options.output}`));
42
+ }
43
+ catch (error) {
44
+ spinner.fail(chalk_1.default.red(`Erreur: ${error.message}`));
45
+ }
46
+ }
@@ -0,0 +1,4 @@
1
+ export declare function push(project: string, options: {
2
+ env: string;
3
+ file: string;
4
+ }): Promise<void>;
@@ -0,0 +1,56 @@
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.push = push;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const ora_1 = __importDefault(require("ora"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ const index_js_1 = require("../index.js");
12
+ const api_js_1 = require("../utils/api.js");
13
+ async function push(project, options) {
14
+ const token = index_js_1.config.get("token");
15
+ if (!token) {
16
+ console.log(chalk_1.default.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
17
+ return;
18
+ }
19
+ // Check if file exists
20
+ if (!fs_1.default.existsSync(options.file)) {
21
+ console.log(chalk_1.default.red(`✗ Fichier non trouvé: ${options.file}`));
22
+ return;
23
+ }
24
+ const spinner = (0, ora_1.default)(`Envoi des variables vers ${project} (${options.env})...`).start();
25
+ try {
26
+ // Parse .env file
27
+ const envContent = fs_1.default.readFileSync(options.file, "utf-8");
28
+ const parsed = dotenv_1.default.parse(envContent);
29
+ const response = await (0, api_js_1.apiRequest)(`/api/v1/projects/${project}/${options.env}`, {
30
+ method: "POST",
31
+ token,
32
+ body: { variables: parsed },
33
+ });
34
+ if (response.error) {
35
+ spinner.fail(chalk_1.default.red(response.error));
36
+ // Suggestion intelligente
37
+ const commonEnvs = ["development", "dev", "staging", "stg", "production", "prod", "test"];
38
+ if (commonEnvs.includes(project.toLowerCase()) || response.error.includes("trouvé")) {
39
+ console.log(chalk_1.default.yellow("\n💡 Astuce : La syntaxe est 'envsafe push <MON_PROJET>'"));
40
+ console.log(chalk_1.default.yellow(` Vous avez essayé de push vers le projet "${project}".`));
41
+ console.log(chalk_1.default.yellow(` Utilisez 'envsafe projects' pour voir la liste de vos projets/slugs.`));
42
+ }
43
+ return;
44
+ }
45
+ spinner.succeed(chalk_1.default.green(`✓ ${response.created} variable(s) créée(s)`));
46
+ if (response.errors?.length > 0) {
47
+ console.log(chalk_1.default.yellow("\nAvertissements:"));
48
+ response.errors.forEach((err) => {
49
+ console.log(chalk_1.default.dim(` - ${err}`));
50
+ });
51
+ }
52
+ }
53
+ catch (error) {
54
+ spinner.fail(chalk_1.default.red(`Erreur: ${error.message}`));
55
+ }
56
+ }
@@ -0,0 +1,3 @@
1
+ export declare function run(project: string, command: string[], options: {
2
+ env: string;
3
+ }): Promise<void>;
@@ -0,0 +1,49 @@
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.run = run;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const child_process_1 = require("child_process");
9
+ const index_js_1 = require("../index.js");
10
+ const api_js_1 = require("../utils/api.js");
11
+ async function run(project, command, options) {
12
+ const token = index_js_1.config.get("token");
13
+ if (!token) {
14
+ console.log(chalk_1.default.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
15
+ process.exit(1);
16
+ }
17
+ console.log(chalk_1.default.dim(`Récupération des variables pour ${project} (${options.env})...`));
18
+ try {
19
+ const response = await (0, api_js_1.apiRequest)(`/api/v1/projects/${project}/${options.env}`, {
20
+ method: "GET",
21
+ token,
22
+ });
23
+ if (response.error) {
24
+ console.log(chalk_1.default.red(`✗ ${response.error}`));
25
+ process.exit(1);
26
+ }
27
+ const variables = response.variables;
28
+ console.log(chalk_1.default.green(`✓ ${Object.keys(variables).length} variable(s) chargée(s)`));
29
+ console.log(chalk_1.default.dim(`Exécution: ${command.join(" ")}\n`));
30
+ // Spawn the command with injected env vars
31
+ const [cmd, ...args] = command;
32
+ const child = (0, child_process_1.spawn)(cmd, args, {
33
+ env: { ...process.env, ...variables },
34
+ stdio: "inherit",
35
+ shell: true,
36
+ });
37
+ child.on("close", (code) => {
38
+ process.exit(code ?? 0);
39
+ });
40
+ child.on("error", (err) => {
41
+ console.error(chalk_1.default.red(`Erreur d'exécution: ${err.message}`));
42
+ process.exit(1);
43
+ });
44
+ }
45
+ catch (error) {
46
+ console.log(chalk_1.default.red(`✗ Erreur: ${error.message}`));
47
+ process.exit(1);
48
+ }
49
+ }
@@ -0,0 +1 @@
1
+ export declare function whoami(): Promise<void>;
@@ -0,0 +1,34 @@
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.whoami = whoami;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const index_js_1 = require("../index.js");
9
+ const api_js_1 = require("../utils/api.js");
10
+ async function whoami() {
11
+ const token = index_js_1.config.get("token");
12
+ const apiUrl = index_js_1.config.get("apiUrl");
13
+ if (!token) {
14
+ console.log(chalk_1.default.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
15
+ return;
16
+ }
17
+ try {
18
+ const user = await (0, api_js_1.apiRequest)("/api/v1/user/me", {
19
+ method: "GET",
20
+ token
21
+ });
22
+ console.log(chalk_1.default.cyan("\n🔐 EnvSafe CLI\n"));
23
+ console.log(` Utilisateur: ${chalk_1.default.white(user.name)} (${chalk_1.default.dim(user.email)})`);
24
+ if (user.lastLoginAt) {
25
+ const date = new Date(user.lastLoginAt).toLocaleString();
26
+ console.log(` Dernière connexion: ${chalk_1.default.dim(date)}`);
27
+ }
28
+ console.log(` API: ${chalk_1.default.dim(apiUrl)}`);
29
+ console.log(` Token: ${chalk_1.default.dim(token.substring(0, 12) + "...")}\n`);
30
+ }
31
+ catch (error) {
32
+ console.log(chalk_1.default.red(`✗ Impossible de récupérer les infos utilisateur: ${error.message}`));
33
+ }
34
+ }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import Conf from "conf";
3
+ export declare const config: Conf<{
4
+ token: unknown;
5
+ apiUrl: unknown;
6
+ dashboardUrl: unknown;
7
+ }>;
package/dist/index.js ADDED
@@ -0,0 +1,98 @@
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
+ exports.config = void 0;
8
+ const commander_1 = require("commander");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const conf_1 = __importDefault(require("conf"));
11
+ const login_js_1 = require("./commands/login.js");
12
+ const pull_js_1 = require("./commands/pull.js");
13
+ const push_js_1 = require("./commands/push.js");
14
+ const run_js_1 = require("./commands/run.js");
15
+ const projects_js_1 = require("./commands/projects.js");
16
+ const whoami_js_1 = require("./commands/whoami.js");
17
+ const package_json_1 = __importDefault(require("../package.json"));
18
+ // Config store for token
19
+ exports.config = new conf_1.default({
20
+ projectName: "envsafe-cli",
21
+ schema: {
22
+ token: { type: "string", default: "" },
23
+ apiUrl: { type: "string", default: "https://envsafe.vercel.app" },
24
+ dashboardUrl: { type: "string", default: "https://envsafe.vercel.app" },
25
+ },
26
+ });
27
+ const program = new commander_1.Command();
28
+ program
29
+ .name("envsafe")
30
+ .description(chalk_1.default.cyan("🔐 EnvSafe CLI - Gestionnaire sécurisé de variables d'environnement"))
31
+ .version(package_json_1.default.version);
32
+ // Login command
33
+ program
34
+ .command("login")
35
+ .description("Se connecter avec un token API")
36
+ .option("-t, --token <token>", "Token API")
37
+ .action(login_js_1.login);
38
+ // Whoami command
39
+ program
40
+ .command("whoami")
41
+ .description("Afficher l'utilisateur connecté")
42
+ .action(whoami_js_1.whoami);
43
+ // Projects command
44
+ program
45
+ .command("projects")
46
+ .alias("ls")
47
+ .description("Lister les projets accessibles")
48
+ .action(projects_js_1.projects);
49
+ // Pull command
50
+ program
51
+ .command("pull")
52
+ .description("Télécharger les variables d'environnement")
53
+ .argument("<project>", "Slug du projet")
54
+ .option("-e, --env <environment>", "Environnement (development, staging, production)", "development")
55
+ .option("-o, --output <file>", "Fichier de sortie", ".env")
56
+ .action(pull_js_1.pull);
57
+ // Push command
58
+ program
59
+ .command("push")
60
+ .description("Envoyer les variables d'environnement")
61
+ .argument("<project>", "Slug du projet")
62
+ .option("-e, --env <environment>", "Environnement", "development")
63
+ .option("-f, --file <file>", "Fichier source", ".env")
64
+ .action(push_js_1.push);
65
+ // Run command
66
+ program
67
+ .command("run")
68
+ .description("Exécuter une commande avec les variables d'environnement injectées")
69
+ .argument("<project>", "Slug du projet")
70
+ .option("-e, --env <environment>", "Environnement", "development")
71
+ .argument("<command...>", "Commande à exécuter")
72
+ .action(run_js_1.run);
73
+ // Config command
74
+ program
75
+ .command("config")
76
+ .description("Gérer la configuration")
77
+ .option("--api-url <url>", "Définir l'URL de l'API EnvSafe")
78
+ .option("--show", "Afficher la configuration actuelle")
79
+ .action((options) => {
80
+ if (options.apiUrl) {
81
+ exports.config.set("apiUrl", options.apiUrl);
82
+ console.log(chalk_1.default.green(`✓ URL de l'API définie: ${options.apiUrl}`));
83
+ }
84
+ if (options.show) {
85
+ console.log(chalk_1.default.cyan("Configuration actuelle:"));
86
+ console.log(` API URL: ${exports.config.get("apiUrl")}`);
87
+ console.log(` Token: ${exports.config.get("token") ? "••••••••" : "(non défini)"}`);
88
+ }
89
+ });
90
+ // Logout command
91
+ program
92
+ .command("logout")
93
+ .description("Se déconnecter")
94
+ .action(() => {
95
+ exports.config.set("token", "");
96
+ console.log(chalk_1.default.green("✓ Déconnexion réussie"));
97
+ });
98
+ program.parse();
@@ -0,0 +1,7 @@
1
+ interface ApiRequestOptions {
2
+ method: "GET" | "POST" | "PUT" | "DELETE";
3
+ token?: string;
4
+ body?: any;
5
+ }
6
+ export declare function apiRequest(endpoint: string, options: ApiRequestOptions): Promise<any>;
7
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apiRequest = apiRequest;
4
+ const index_js_1 = require("../index.js");
5
+ async function apiRequest(endpoint, options) {
6
+ const apiUrl = index_js_1.config.get("apiUrl");
7
+ const token = options.token || index_js_1.config.get("token");
8
+ const url = `${apiUrl}${endpoint}`;
9
+ const headers = {
10
+ "Content-Type": "application/json",
11
+ };
12
+ if (token) {
13
+ headers["Authorization"] = `Bearer ${token}`;
14
+ }
15
+ const response = await fetch(url, {
16
+ method: options.method,
17
+ headers,
18
+ body: options.body ? JSON.stringify(options.body) : undefined,
19
+ });
20
+ const data = await response.json();
21
+ if (!response.ok && !data.error) {
22
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
23
+ }
24
+ return data;
25
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@envsafes-org/cli",
3
+ "version": "0.0.6",
4
+ "description": "CLI pour EnvSafe - Gestionnaire sécurisé de variables d'environnement",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "envsafe": "./dist/index.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Ifiboys/envsafe-cli.git"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsx src/index.ts",
16
+ "start": "node dist/index.js"
17
+ },
18
+ "keywords": [
19
+ "env",
20
+ "environment",
21
+ "variables",
22
+ "cli",
23
+ "secrets"
24
+ ],
25
+ "author": "EnvSafe Team",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "chalk": "^5.3.0",
29
+ "commander": "^12.0.0",
30
+ "conf": "^12.0.0",
31
+ "dotenv": "^16.3.1",
32
+ "inquirer": "^9.2.12",
33
+ "open": "^11.0.0",
34
+ "ora": "^8.0.1"
35
+ },
36
+ "devDependencies": {
37
+ "@types/inquirer": "^9.0.7",
38
+ "@types/node": "^20.0.0",
39
+ "tsx": "^4.0.0",
40
+ "typescript": "^5.0.0"
41
+ }
42
+ }
@@ -0,0 +1,58 @@
1
+ import chalk from "chalk";
2
+ import inquirer from "inquirer";
3
+ import { config } from "../index.js";
4
+ import { apiRequest } from "../utils/api.js";
5
+
6
+ import open from "open";
7
+
8
+ export async function login(options: { token?: string }) {
9
+ let token = options.token;
10
+
11
+ if (!token) {
12
+ const dashboardUrl = config.get("dashboardUrl") as string;
13
+ const loginUrl = `${dashboardUrl}/dashboard?redirect=tokens`;
14
+
15
+ console.log(chalk.cyan(`\nOuverture du navigateur pour générer un token...\n`));
16
+ console.log(chalk.dim(`Si le navigateur ne s'ouvre pas, visitez : ${loginUrl}`));
17
+
18
+ try {
19
+ await open(loginUrl);
20
+ } catch (err) {
21
+ // Ignore error if browser fails to open
22
+ }
23
+
24
+ const answers = await inquirer.prompt([
25
+ {
26
+ type: "password",
27
+ name: "token",
28
+ message: "Collez votre token API EnvSafe ici:",
29
+ mask: "*",
30
+ validate: (input) => input.length > 0 || "Le token est requis",
31
+ },
32
+ ]);
33
+ token = answers.token;
34
+ }
35
+
36
+ console.log(chalk.dim("Vérification du token..."));
37
+
38
+ try {
39
+ // Test the token
40
+ const response = await apiRequest("/api/v1/projects", {
41
+ method: "GET",
42
+ token,
43
+ });
44
+
45
+ if (response.error) {
46
+ console.log(chalk.red(`✗ Erreur: ${response.error}`));
47
+ return;
48
+ }
49
+
50
+ // Save token
51
+ config.set("token", token);
52
+
53
+ console.log(chalk.green("✓ Connexion réussie!"));
54
+ console.log(chalk.dim(` ${response.count} projet(s) accessible(s)`));
55
+ } catch (error: any) {
56
+ console.log(chalk.red(`✗ Erreur de connexion: ${error.message}`));
57
+ }
58
+ }
@@ -0,0 +1,40 @@
1
+ import chalk from "chalk";
2
+ import { config } from "../index.js";
3
+ import { apiRequest } from "../utils/api.js";
4
+
5
+ export async function projects() {
6
+ const token = config.get("token") as string;
7
+
8
+ if (!token) {
9
+ console.log(chalk.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
10
+ return;
11
+ }
12
+
13
+ try {
14
+ const response = await apiRequest("/api/v1/projects", {
15
+ method: "GET",
16
+ token,
17
+ });
18
+
19
+ if (response.error) {
20
+ console.log(chalk.red(`✗ ${response.error}`));
21
+ return;
22
+ }
23
+
24
+ console.log(chalk.cyan("\n📁 Projets accessibles\n"));
25
+
26
+ if (response.projects.length === 0) {
27
+ console.log(chalk.dim(" Aucun projet trouvé."));
28
+ return;
29
+ }
30
+
31
+ for (const project of response.projects) {
32
+ console.log(chalk.bold(` ${project.name}`));
33
+ console.log(chalk.dim(` slug: ${project.slug}`));
34
+ console.log(chalk.dim(` envs: ${project.environments.join(", ")}`));
35
+ console.log("");
36
+ }
37
+ } catch (error: any) {
38
+ console.log(chalk.red(`✗ Erreur: ${error.message}`));
39
+ }
40
+ }
@@ -0,0 +1,53 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import fs from "fs";
4
+ import { config } from "../index.js";
5
+ import { apiRequest } from "../utils/api.js";
6
+
7
+ export async function pull(
8
+ project: string,
9
+ options: { env: string; output: string }
10
+ ) {
11
+ const token = config.get("token") as string;
12
+
13
+ if (!token) {
14
+ console.log(chalk.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
15
+ return;
16
+ }
17
+
18
+ const spinner = ora(`Téléchargement des variables pour ${project} (${options.env})...`).start();
19
+
20
+ try {
21
+ const response = await apiRequest(`/api/v1/projects/${project}/${options.env}`, {
22
+ method: "GET",
23
+ token,
24
+ });
25
+
26
+ if (response.error) {
27
+ spinner.fail(chalk.red(response.error));
28
+
29
+ // Suggestion intelligente si l'utilisateur a confondu projet et environnement
30
+ const commonEnvs = ["development", "dev", "staging", "stg", "production", "prod", "test"];
31
+ if (commonEnvs.includes(project.toLowerCase()) || response.error.includes("trouvé")) {
32
+ console.log(chalk.yellow("\n💡 Astuce : La syntaxe est 'envsafe pull <MON_PROJET>'"));
33
+ console.log(chalk.yellow(` Vous avez essayé de pull le projet "${project}".`));
34
+ console.log(chalk.yellow(` Utilisez 'envsafe projects' pour voir la liste de vos projets/slugs.`));
35
+ }
36
+ return;
37
+ }
38
+
39
+ // Format as .env content
40
+ const envContent = Object.entries(response.variables as Record<string, string>)
41
+ .map(([key, value]) => `${key}="${value}"`)
42
+ .join("\n");
43
+
44
+ // Write to file
45
+ fs.writeFileSync(options.output, envContent + "\n");
46
+
47
+ spinner.succeed(
48
+ chalk.green(`✓ ${response.count} variable(s) téléchargée(s) vers ${options.output}`)
49
+ );
50
+ } catch (error: any) {
51
+ spinner.fail(chalk.red(`Erreur: ${error.message}`));
52
+ }
53
+ }