@envsafes-org/cli 0.0.6 → 0.0.7

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/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@envsafes-org/cli",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "CLI pour EnvSafe - Gestionnaire sécurisé de variables d'environnement",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "envsafe": "./dist/index.js"
8
8
  },
9
+ "files": [
10
+ "dist"
11
+ ],
9
12
  "repository": {
10
13
  "type": "git",
11
14
  "url": "git+https://github.com/Ifiboys/envsafe-cli.git"
@@ -1,328 +0,0 @@
1
- # EnvSafe Storage Agent (BYOS)
2
-
3
- Agent simple pour héberger vos secrets chiffrés EnvSafe sur votre propre infrastructure.
4
-
5
- ## Qu'est-ce que c'est ?
6
-
7
- Cet agent vous permet de stocker vos secrets chiffrés sur **votre propre serveur** au lieu du cloud EnvSafe, tout en continuant à utiliser l'interface EnvSafe et la CLI.
8
-
9
- ## ⚠ Rappel Important de Sécurité
10
-
11
- **Même avec cet agent sur votre serveur, vos données sont DÉJÀ chiffrées (AES-256-GCM).**
12
-
13
- - **Cet agent ne peut PAS lire vos secrets**
14
- - Il stocke uniquement du texte illisible
15
- - **Seule votre clé privée locale peut déchiffrer les données**
16
- - EnvSafe (SaaS) ne peut pas non plus lire vos secrets
17
-
18
- ## Installation
19
-
20
- ### Prérequis
21
-
22
- - Docker et Docker Compose
23
- - Node.js 18+ (si vous lancez sans Docker)
24
-
25
- ### Avec Docker (Recommandé)
26
-
27
- ```bash
28
- # Cloner ce dépôt
29
- git clone https://github.com/Ifiboys/envsafe-storage-agent
30
- cd envsafe-storage-agent
31
-
32
- # Configurer les variables d'environnement
33
- cp .env.example .env
34
- # Éditez .env et définissez AUTH_SECRET avec une valeur très sécurisée
35
-
36
- # Lancer l'agent
37
- docker-compose up -d
38
- ```
39
-
40
- ### Sans Docker
41
-
42
- ```bash
43
- npm install
44
- npm run build
45
- npm start
46
- ```
47
-
48
- ## Configuration
49
-
50
- ### Variables d'environnement
51
-
52
- ```bash
53
- # SECRET PARTAGÉ - Utilisez un générateur de mots de passe sécurisé
54
- AUTH_SECRET=your-very-long-and-secure-secret-here
55
-
56
- # Port d'écoute
57
- PORT=3001
58
-
59
- # Base de données (SQLite par défaut, PostgreSQL recommandé en production)
60
- DATABASE_URL=file:./envsafe-storage.db
61
- # Exemple PostgreSQL:
62
- # DATABASE_URL=postgresql://user:password@localhost:5432/envsafe_storage
63
- ```
64
-
65
- ### Génération du secret
66
-
67
- ```bash
68
- # Utilisez cette commande pour générer un secret sécurisé:
69
- openssl rand -hex 32
70
- ```
71
-
72
- ## Configuration dans EnvSafe
73
-
74
- 1. Allez dans votre projet EnvSafe
75
- 2. Paramètres → Stockage
76
- 3. Activez **"Stockage Externe (BYOS)"**
77
- 4. Renseignez:
78
- - **URL**: `https://votre-serveur.com` (l'URL où votre agent est accessible)
79
- - **Secret**: Le même `AUTH_SECRET` que vous avez défini dans `.env`
80
- 5. Cliquez sur **"Tester la connexion"**
81
- 6. Si le test est réussi, sauvegardez
82
-
83
- ## API Endpoints
84
-
85
- L'agent expose ces routes (toutes protégées par authentification):
86
-
87
- - `GET /health` - Vérification de la santé de l'agent
88
- - `POST /store-secret` - Stocke un secret chiffré
89
- - `GET /get-secret` - Récupère un secret chiffré
90
- - `GET /get-all-secrets` - Récupère tous les secrets d'un environnement
91
- - `DELETE /delete-secret` - Supprime un secret
92
-
93
- ## Exemple de Code de l'Agent
94
-
95
- Voici un exemple complet d'agent de stockage (Node.js + Express):
96
-
97
- ```typescript
98
- import express, { Request, Response } from "express";
99
- import crypto from "crypto";
100
- import { PrismaClient } from "@prisma/client";
101
-
102
- const app = express();
103
- const prisma = new PrismaClient();
104
-
105
- app.use(express.json());
106
-
107
- // ⚠️ IMPORTANT: Cet agent stocke UNIQUEMENT des données CHIFFRÉES
108
- // Il ne peut PAS lire vos secrets
109
-
110
- // Middleware d'authentification
111
- const authenticate = (req: Request, res: Response, next: Function) => {
112
- const authHeader = req.headers.authorization;
113
- const timestamp = req.headers["x-envsafe-timestamp"] as string;
114
- const signature = req.headers["x-envsafe-signature"] as string;
115
-
116
- if (!authHeader || !timestamp || !signature) {
117
- return res.status(401).json({ error: "Missing authentication headers" });
118
- }
119
-
120
- const token = authHeader.replace("Bearer ", "");
121
- const expectedSecret = process.env.AUTH_SECRET;
122
-
123
- if (!expectedSecret) {
124
- console.error("AUTH_SECRET not configured");
125
- return res.status(500).json({ error: "Server misconfiguration" });
126
- }
127
-
128
- // Vérifier le token
129
- if (token !== expectedSecret) {
130
- console.warn(`Failed authentication attempt from ${req.ip}`);
131
- return res.status(401).json({ error: "Invalid token" });
132
- }
133
-
134
- // Vérifier la signature HMAC
135
- const expectedSignature = crypto
136
- .createHmac("sha256", expectedSecret)
137
- .update(timestamp)
138
- .digest("hex");
139
-
140
- if (signature !== expectedSignature) {
141
- console.warn(`Invalid HMAC signature from ${req.ip}`);
142
- return res.status(401).json({ error: "Invalid signature" });
143
- }
144
-
145
- // Vérifier que le timestamp n'est pas trop ancien (5 minutes max)
146
- const now = Date.now();
147
- const requestTime = parseInt(timestamp);
148
- if (Math.abs(now - requestTime) > 5 * 60 * 1000) {
149
- return res.status(401).json({ error: "Request expired" });
150
- }
151
-
152
- next();
153
- };
154
-
155
- // Health check
156
- app.get("/health", authenticate, (req: Request, res: Response) => {
157
- res.json({
158
- status: "healthy",
159
- timestamp: new Date().toISOString(),
160
- version: "1.0.0"
161
- });
162
- });
163
-
164
- // Stocker un secret chiffré
165
- app.post("/store-secret", authenticate, async (req: Request, res: Response) => {
166
- const { key, encryptedValue, environmentId } = req.body;
167
-
168
- if (!key || !encryptedValue || !environmentId) {
169
- return res.status(400).json({ error: "Missing required fields" });
170
- }
171
-
172
- try {
173
- // Note: encryptedValue est déjà chiffré par le client
174
- // L'agent ne fait que le stocker, il ne peut pas le lire
175
- await prisma.encryptedVariable.upsert({
176
- where: {
177
- environmentId_key: { environmentId, key }
178
- },
179
- create: { key, encryptedValue, environmentId },
180
- update: { encryptedValue }
181
- });
182
-
183
- console.log(`✅ Stored encrypted variable: ${key} for env: ${environmentId}`);
184
- res.json({ success: true });
185
- } catch (error) {
186
- console.error("Error storing variable:", error);
187
- res.status(500).json({ error: "Failed to store variable" });
188
- }
189
- });
190
-
191
- // Récupérer un secret chiffré
192
- app.get("/get-secret", authenticate, async (req: Request, res: Response) => {
193
- const { key, environmentId } = req.query;
194
-
195
- if (!key || !environmentId) {
196
- return res.status(400).json({ error: "Missing required parameters" });
197
- }
198
-
199
- try {
200
- const variable = await prisma.encryptedVariable.findUnique({
201
- where: {
202
- environmentId_key: {
203
- environmentId: environmentId as string,
204
- key: key as string
205
- }
206
- }
207
- });
208
-
209
- if (!variable) {
210
- return res.status(404).json({ error: "Variable not found" });
211
- }
212
-
213
- // Retourne la valeur chiffrée (toujours illisible)
214
- res.json({ encryptedValue: variable.encryptedValue });
215
- } catch (error) {
216
- console.error("Error getting variable:", error);
217
- res.status(500).json({ error: "Failed to get variable" });
218
- }
219
- });
220
-
221
- const PORT = process.env.PORT || 3001;
222
-
223
- app.listen(PORT, () => {
224
- console.log(`🔐 EnvSafe Storage Agent running on port ${PORT}`);
225
- console.log(`⚠️ Reminder: This agent stores ENCRYPTED data only`);
226
- console.log(`⚠️ Even with database access, secrets remain unreadable`);
227
- });
228
- ```
229
-
230
- ### Schéma Prisma pour l'agent
231
-
232
- ```prisma
233
- // prisma/schema.prisma (pour l'agent de stockage)
234
-
235
- datasource db {
236
- provider = "postgresql"
237
- url = env("DATABASE_URL")
238
- }
239
-
240
- generator client {
241
- provider = "prisma-client-js"
242
- }
243
-
244
- model EncryptedVariable {
245
- id String @id @default(cuid())
246
- key String
247
- encryptedValue String @map("encrypted_value") // Toujours chiffré (AES-256-GCM)
248
- environmentId String @map("environment_id")
249
-
250
- createdAt DateTime @default(now()) @map("created_at")
251
- updatedAt DateTime @updatedAt @map("updated_at")
252
-
253
- @@unique([environmentId, key])
254
- @@map("encrypted_variables")
255
- }
256
- ```
257
-
258
- ## Sécurité
259
-
260
- ### Authentification
261
-
262
- Toutes les requêtes doivent inclure:
263
- - `Authorization: Bearer <AUTH_SECRET>`
264
- - `X-EnvSafe-Timestamp: <timestamp>`
265
- - `X-EnvSafe-Signature: <HMAC-SHA256>`
266
-
267
- ### Ce que l'agent NE FAIT PAS
268
-
269
- - ❌ Il ne stocke PAS de secrets en clair
270
- - ❌ Il ne peut PAS déchiffrer vos données
271
- - ❌ Il n'a PAS accès à vos clés de déchiffrement
272
-
273
- ### Ce qu'il FAIT
274
-
275
- - ✅ Il stocke des blobs chiffrés (texte illisible)
276
- - ✅ Il vérifie l'authentification HMAC des requêtes
277
- - ✅ Il refuse les requêtes non autorisées
278
-
279
- ## Production
280
-
281
- ### Recommandations
282
-
283
- 1. **HTTPS Obligatoire** : Utilisez un reverse proxy (Nginx, Caddy) avec certificat SSL
284
- 2. **Base de données externe** : Utilisez PostgreSQL au lieu de SQLite
285
- 3. **Firewall** : Limitez l'accès à l'IP du serveur EnvSafe
286
- 4. **Backups** : Configurez des sauvegardes régulières de votre base de données
287
- 5. **Monitoring** : Surveillez les logs et les tentatives d'accès
288
-
289
- ### Exemple Nginx
290
-
291
- ```nginx
292
- server {
293
- listen 443 ssl http2;
294
- server_name envsafe-storage.votre-domaine.com;
295
-
296
- ssl_certificate /etc/letsencrypt/live/votre-domaine.com/fullchain.pem;
297
- ssl_certificate_key /etc/letsencrypt/live/votre-domaine.com/privkey.pem;
298
-
299
- location / {
300
- proxy_pass http://localhost:3001;
301
- proxy_set_header Host $host;
302
- proxy_set_header X-Real-IP $remote_addr;
303
- }
304
- }
305
- ```
306
-
307
- ## Logs
308
-
309
- Les logs de l'agent incluent:
310
- - Tentatives d'authentification (succès/échecs)
311
- - Opérations de stockage/récupération
312
- - Erreurs système
313
-
314
- ```bash
315
- # Voir les logs en temps réel
316
- docker-compose logs -f
317
- ```
318
-
319
- ## Support
320
-
321
- Pour toute question ou problème:
322
- - Documentation: https://envsafe.vercel.app/docs
323
- - Issues: https://github.com/Ifiboys/envsafe-storage-agent/issues
324
- - Email: oladokunefi123@gmail.com
325
-
326
- ## Licence
327
-
328
- MIT - Voir LICENSE
@@ -1,58 +0,0 @@
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
- }
@@ -1,40 +0,0 @@
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
- }
@@ -1,53 +0,0 @@
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
- }
@@ -1,64 +0,0 @@
1
- import chalk from "chalk";
2
- import ora from "ora";
3
- import fs from "fs";
4
- import dotenv from "dotenv";
5
- import { config } from "../index.js";
6
- import { apiRequest } from "../utils/api.js";
7
-
8
- export async function push(
9
- project: string,
10
- options: { env: string; file: string }
11
- ) {
12
- const token = config.get("token") as string;
13
-
14
- if (!token) {
15
- console.log(chalk.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
16
- return;
17
- }
18
-
19
- // Check if file exists
20
- if (!fs.existsSync(options.file)) {
21
- console.log(chalk.red(`✗ Fichier non trouvé: ${options.file}`));
22
- return;
23
- }
24
-
25
- const spinner = ora(`Envoi des variables vers ${project} (${options.env})...`).start();
26
-
27
- try {
28
- // Parse .env file
29
- const envContent = fs.readFileSync(options.file, "utf-8");
30
- const parsed = dotenv.parse(envContent);
31
-
32
- const response = await apiRequest(`/api/v1/projects/${project}/${options.env}`, {
33
- method: "POST",
34
- token,
35
- body: { variables: parsed },
36
- });
37
-
38
- if (response.error) {
39
- spinner.fail(chalk.red(response.error));
40
-
41
- // Suggestion intelligente
42
- const commonEnvs = ["development", "dev", "staging", "stg", "production", "prod", "test"];
43
- if (commonEnvs.includes(project.toLowerCase()) || response.error.includes("trouvé")) {
44
- console.log(chalk.yellow("\n💡 Astuce : La syntaxe est 'envsafe push <MON_PROJET>'"));
45
- console.log(chalk.yellow(` Vous avez essayé de push vers le projet "${project}".`));
46
- console.log(chalk.yellow(` Utilisez 'envsafe projects' pour voir la liste de vos projets/slugs.`));
47
- }
48
- return;
49
- }
50
-
51
- spinner.succeed(
52
- chalk.green(`✓ ${response.created} variable(s) créée(s)`)
53
- );
54
-
55
- if (response.errors?.length > 0) {
56
- console.log(chalk.yellow("\nAvertissements:"));
57
- response.errors.forEach((err: string) => {
58
- console.log(chalk.dim(` - ${err}`));
59
- });
60
- }
61
- } catch (error: any) {
62
- spinner.fail(chalk.red(`Erreur: ${error.message}`));
63
- }
64
- }
@@ -1,55 +0,0 @@
1
- import chalk from "chalk";
2
- import { spawn } from "child_process";
3
- import { config } from "../index.js";
4
- import { apiRequest } from "../utils/api.js";
5
-
6
- export async function run(
7
- project: string,
8
- command: string[],
9
- options: { env: 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
- process.exit(1);
16
- }
17
-
18
- console.log(chalk.dim(`Récupération des variables pour ${project} (${options.env})...`));
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
- console.log(chalk.red(`✗ ${response.error}`));
28
- process.exit(1);
29
- }
30
-
31
- const variables = response.variables as Record<string, string>;
32
- console.log(chalk.green(`✓ ${Object.keys(variables).length} variable(s) chargée(s)`));
33
- console.log(chalk.dim(`Exécution: ${command.join(" ")}\n`));
34
-
35
- // Spawn the command with injected env vars
36
- const [cmd, ...args] = command;
37
- const child = spawn(cmd, args, {
38
- env: { ...process.env, ...variables },
39
- stdio: "inherit",
40
- shell: true,
41
- });
42
-
43
- child.on("close", (code) => {
44
- process.exit(code ?? 0);
45
- });
46
-
47
- child.on("error", (err) => {
48
- console.error(chalk.red(`Erreur d'exécution: ${err.message}`));
49
- process.exit(1);
50
- });
51
- } catch (error: any) {
52
- console.log(chalk.red(`✗ Erreur: ${error.message}`));
53
- process.exit(1);
54
- }
55
- }
@@ -1,33 +0,0 @@
1
- import chalk from "chalk";
2
- import { config } from "../index.js";
3
- import { apiRequest } from "../utils/api.js";
4
-
5
- export async function whoami() {
6
- const token = config.get("token") as string;
7
- const apiUrl = config.get("apiUrl") as string;
8
-
9
- if (!token) {
10
- console.log(chalk.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
11
- return;
12
- }
13
-
14
- try {
15
- const user = await apiRequest("/api/v1/user/me", {
16
- method: "GET",
17
- token
18
- });
19
-
20
- console.log(chalk.cyan("\n🔐 EnvSafe CLI\n"));
21
- console.log(` Utilisateur: ${chalk.white(user.name)} (${chalk.dim(user.email)})`);
22
-
23
- if (user.lastLoginAt) {
24
- const date = new Date(user.lastLoginAt).toLocaleString();
25
- console.log(` Dernière connexion: ${chalk.dim(date)}`);
26
- }
27
-
28
- console.log(` API: ${chalk.dim(apiUrl)}`);
29
- console.log(` Token: ${chalk.dim(token.substring(0, 12) + "...")}\n`);
30
- } catch (error: any) {
31
- console.log(chalk.red(`✗ Impossible de récupérer les infos utilisateur: ${error.message}`));
32
- }
33
- }
package/src/index.ts DELETED
@@ -1,105 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from "commander";
4
- import chalk from "chalk";
5
- import Conf from "conf";
6
- import { login } from "./commands/login.js";
7
- import { pull } from "./commands/pull.js";
8
- import { push } from "./commands/push.js";
9
- import { run } from "./commands/run.js";
10
- import { projects } from "./commands/projects.js";
11
- import { whoami } from "./commands/whoami.js";
12
- import pkg from "../package.json";
13
-
14
- // Config store for token
15
- export const config = new Conf({
16
- projectName: "envsafe-cli",
17
- schema: {
18
- token: { type: "string", default: "" },
19
- apiUrl: { type: "string", default: "https://envsafe.vercel.app" },
20
- dashboardUrl: { type: "string", default: "https://envsafe.vercel.app" },
21
- },
22
- });
23
-
24
- const program = new Command();
25
-
26
- program
27
- .name("envsafe")
28
- .description(chalk.cyan("🔐 EnvSafe CLI - Gestionnaire sécurisé de variables d'environnement"))
29
- .version(pkg.version);
30
-
31
- // Login command
32
- program
33
- .command("login")
34
- .description("Se connecter avec un token API")
35
- .option("-t, --token <token>", "Token API")
36
- .action(login);
37
-
38
- // Whoami command
39
- program
40
- .command("whoami")
41
- .description("Afficher l'utilisateur connecté")
42
- .action(whoami);
43
-
44
- // Projects command
45
- program
46
- .command("projects")
47
- .alias("ls")
48
- .description("Lister les projets accessibles")
49
- .action(projects);
50
-
51
- // Pull command
52
- program
53
- .command("pull")
54
- .description("Télécharger les variables d'environnement")
55
- .argument("<project>", "Slug du projet")
56
- .option("-e, --env <environment>", "Environnement (development, staging, production)", "development")
57
- .option("-o, --output <file>", "Fichier de sortie", ".env")
58
- .action(pull);
59
-
60
- // Push command
61
- program
62
- .command("push")
63
- .description("Envoyer les variables d'environnement")
64
- .argument("<project>", "Slug du projet")
65
- .option("-e, --env <environment>", "Environnement", "development")
66
- .option("-f, --file <file>", "Fichier source", ".env")
67
- .action(push);
68
-
69
- // Run command
70
- program
71
- .command("run")
72
- .description("Exécuter une commande avec les variables d'environnement injectées")
73
- .argument("<project>", "Slug du projet")
74
- .option("-e, --env <environment>", "Environnement", "development")
75
- .argument("<command...>", "Commande à exécuter")
76
- .action(run);
77
-
78
- // Config command
79
- program
80
- .command("config")
81
- .description("Gérer la configuration")
82
- .option("--api-url <url>", "Définir l'URL de l'API EnvSafe")
83
- .option("--show", "Afficher la configuration actuelle")
84
- .action((options) => {
85
- if (options.apiUrl) {
86
- config.set("apiUrl", options.apiUrl);
87
- console.log(chalk.green(`✓ URL de l'API définie: ${options.apiUrl}`));
88
- }
89
- if (options.show) {
90
- console.log(chalk.cyan("Configuration actuelle:"));
91
- console.log(` API URL: ${config.get("apiUrl")}`);
92
- console.log(` Token: ${config.get("token") ? "••••••••" : "(non défini)"}`);
93
- }
94
- });
95
-
96
- // Logout command
97
- program
98
- .command("logout")
99
- .description("Se déconnecter")
100
- .action(() => {
101
- config.set("token", "");
102
- console.log(chalk.green("✓ Déconnexion réussie"));
103
- });
104
-
105
- program.parse();
package/src/utils/api.ts DELETED
@@ -1,36 +0,0 @@
1
- import { config } from "../index.js";
2
-
3
- interface ApiRequestOptions {
4
- method: "GET" | "POST" | "PUT" | "DELETE";
5
- token?: string;
6
- body?: any;
7
- }
8
-
9
- export async function apiRequest(endpoint: string, options: ApiRequestOptions) {
10
- const apiUrl = config.get("apiUrl") as string;
11
- const token = options.token || (config.get("token") as string);
12
-
13
- const url = `${apiUrl}${endpoint}`;
14
-
15
- const headers: Record<string, string> = {
16
- "Content-Type": "application/json",
17
- };
18
-
19
- if (token) {
20
- headers["Authorization"] = `Bearer ${token}`;
21
- }
22
-
23
- const response = await fetch(url, {
24
- method: options.method,
25
- headers,
26
- body: options.body ? JSON.stringify(options.body) : undefined,
27
- });
28
-
29
- const data = await response.json();
30
-
31
- if (!response.ok && !data.error) {
32
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
33
- }
34
-
35
- return data;
36
- }
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "declaration": true,
13
- "resolveJsonModule": true
14
- },
15
- "include": [
16
- "src/**/*"
17
- ],
18
- "exclude": [
19
- "node_modules",
20
- "dist"
21
- ]
22
- }