@elding/cli 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/commands/proxy.js +5 -1
- package/dist/commands/run.js +8 -1
- package/dist/index.js +6 -2
- package/dist/lib/apiUrl.js +2 -2
- package/dist/lib/proxyServer.js +3 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @elding/cli
|
|
2
|
+
|
|
3
|
+
Le CLI Elding. Lance ton app en local avec tes clés API injectées par un proxy :
|
|
4
|
+
la vraie clé n'entre jamais dans la mémoire de ton app.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g @elding/cli
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Démarrage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
elding login # connexion (ouvre le navigateur)
|
|
14
|
+
elding init # lie le dossier courant à un set de secrets
|
|
15
|
+
elding proxy -- npm run dev # lance ton app avec le proxy actif
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Dans ton code, utilise [`@elding/sdk`](https://www.npmjs.com/package/@elding/sdk)
|
|
19
|
+
(`configure()` pour les clés HTTP, `secret()` pour le reste).
|
|
20
|
+
|
|
21
|
+
## Commandes
|
|
22
|
+
|
|
23
|
+
| Commande | Rôle |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `elding login` / `logout` | Connexion / déconnexion (token dans le keychain de l'OS) |
|
|
26
|
+
| `elding init` | Lie le projet à une organisation + un set |
|
|
27
|
+
| `elding proxy -- <cmd>` | Lance `<cmd>` avec le proxy local (clé hors process) |
|
|
28
|
+
| `elding run -- <cmd>` | Injecte les secrets en variables d'env (si autorisé par l'org) |
|
|
29
|
+
| `elding keys` / `sets` | Liste les clés / les sets |
|
|
30
|
+
| `elding use` | Change le set actif |
|
|
31
|
+
| `elding status` / `whoami` | État courant / utilisateur connecté |
|
|
32
|
+
| `elding doctor` | Diagnostique la config locale |
|
|
33
|
+
| `elding open` | Ouvre le dashboard web |
|
|
34
|
+
|
|
35
|
+
## `proxy` vs `run`
|
|
36
|
+
|
|
37
|
+
- **`proxy`** (recommandé) : ton app reçoit un placeholder, le proxy injecte la
|
|
38
|
+
vraie clé dans la requête sortante et la verrouille à son domaine. La clé
|
|
39
|
+
n'entre jamais dans `process.env`. Réservé aux **clés API HTTP**.
|
|
40
|
+
- **`run`** : injecte les secrets en clair dans `process.env` (tous types, tous
|
|
41
|
+
langages). Désactivé par défaut côté organisation, à activer explicitement.
|
|
42
|
+
|
|
43
|
+
## Sécurité
|
|
44
|
+
|
|
45
|
+
- Token stocké dans le **keychain de l'OS**, jamais en clair sur disque.
|
|
46
|
+
- Le proxy bind `127.0.0.1` uniquement, anti-SSRF, valeurs de secrets jamais loggées.
|
|
47
|
+
- Chaque clé peut être verrouillée à un domaine (`allowedHost`) et coupée (`INACTIVE`).
|
package/dist/commands/proxy.js
CHANGED
|
@@ -43,10 +43,14 @@ async function proxy(cmd, args, options = {}) {
|
|
|
43
43
|
spinner.succeed(chalk.green(`Proxy actif sur ${server.url} — ${Object.keys(secrets).length} secret(s) pour ${(0, terminal_js_1.safeText)(project.setName)}`));
|
|
44
44
|
console.log(chalk.dim("Les clés restent dans le proxy, jamais dans la mémoire de l'app."));
|
|
45
45
|
if (!options.reportLogs)
|
|
46
|
-
console.log(chalk.dim("Logs cloud proxy désactivés
|
|
46
|
+
console.log(chalk.dim("Logs cloud proxy désactivés (--no-report-logs)."));
|
|
47
|
+
else
|
|
48
|
+
console.log(chalk.dim("Métadonnées de requêtes envoyées au vault (jamais les valeurs). Couper avec --no-report-logs."));
|
|
47
49
|
const child = (0, child_process_1.spawn)(cmd, args, {
|
|
48
50
|
env: {
|
|
49
51
|
...process.env,
|
|
52
|
+
// Résout les binaires locaux (next, vite...) comme le ferait npm.
|
|
53
|
+
PATH: `${process.cwd()}/node_modules/.bin:${process.env.PATH ?? ""}`,
|
|
50
54
|
ELDING_PROXY_URL: server.url,
|
|
51
55
|
ELDING_PROXY_TOKEN: server.token,
|
|
52
56
|
},
|
package/dist/commands/run.js
CHANGED
|
@@ -35,11 +35,18 @@ async function run(cmd, args, options = {}) {
|
|
|
35
35
|
spinner.fail(chalk.red((0, terminal_js_1.safeError)(err)));
|
|
36
36
|
process.exit(1);
|
|
37
37
|
}
|
|
38
|
+
console.error(chalk.yellow("⚠ Mode run : les clés sont injectées en clair dans process.env (lisibles par votre IA et vos dépendances)."));
|
|
39
|
+
console.error(chalk.dim(" Pour que les clés n'entrent jamais dans votre app, préférez `elding proxy`."));
|
|
38
40
|
const { env: safeSecrets, rejected } = (0, env_js_1.filterSecretsForEnv)(secrets);
|
|
39
41
|
if (rejected.length > 0) {
|
|
40
42
|
throw new Error(`Secrets refuses car leurs noms sont dangereux pour l'environnement: ${rejected.map((name) => (0, terminal_js_1.safeText)(name, 80)).join(", ")}`);
|
|
41
43
|
}
|
|
42
|
-
const env = {
|
|
44
|
+
const env = {
|
|
45
|
+
...process.env,
|
|
46
|
+
// Résout les binaires locaux (next, vite...) comme le ferait npm.
|
|
47
|
+
PATH: `${process.cwd()}/node_modules/.bin:${process.env.PATH ?? ""}`,
|
|
48
|
+
...safeSecrets,
|
|
49
|
+
};
|
|
43
50
|
const result = (0, child_process_1.spawnSync)(cmd, args, {
|
|
44
51
|
env,
|
|
45
52
|
stdio: "inherit",
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,9 @@ const program = new commander_1.Command();
|
|
|
18
18
|
program
|
|
19
19
|
.name("elding")
|
|
20
20
|
.description("Elding CLI — secrets depuis le vault, zéro .env")
|
|
21
|
-
.version("0.8.
|
|
21
|
+
.version("0.8.2")
|
|
22
|
+
// Permet aux sous-commandes de passer les flags de la commande wrappée (next dev --turbopack...).
|
|
23
|
+
.enablePositionalOptions();
|
|
22
24
|
program
|
|
23
25
|
.command("login")
|
|
24
26
|
.description("Authentifier le CLI via le navigateur")
|
|
@@ -41,6 +43,7 @@ program
|
|
|
41
43
|
.command("run <cmd> [args...]")
|
|
42
44
|
.description("Lancer une commande avec les secrets injectés en variables d'environnement")
|
|
43
45
|
.option("--shell", "Executer via le shell systeme (a utiliser seulement si necessaire)")
|
|
46
|
+
.passThroughOptions()
|
|
44
47
|
.allowUnknownOption()
|
|
45
48
|
.action(async (cmd, args = [], options) => {
|
|
46
49
|
await (0, run_js_1.run)(cmd, args, { shell: !!options.shell }).catch((err) => {
|
|
@@ -52,8 +55,9 @@ program
|
|
|
52
55
|
.command("proxy <cmd> [args...]")
|
|
53
56
|
.description("Lancer une commande derrière un proxy local qui injecte les clés — jamais en mémoire de l'app")
|
|
54
57
|
.option("-v, --verbose", "Logger chaque requête (méthode/host/status/latence)")
|
|
55
|
-
.option("--report-logs", "
|
|
58
|
+
.option("--no-report-logs", "Ne pas envoyer les metadonnees de requetes proxy au vault")
|
|
56
59
|
.option("--shell", "Executer via le shell systeme (a utiliser seulement si necessaire)")
|
|
60
|
+
.passThroughOptions()
|
|
57
61
|
.allowUnknownOption()
|
|
58
62
|
.action(async (cmd, args = [], options) => {
|
|
59
63
|
await (0, proxy_js_1.proxy)(cmd, args, {
|
package/dist/lib/apiUrl.js
CHANGED
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.resolveBaseUrl = resolveBaseUrl;
|
|
7
7
|
const net_1 = __importDefault(require("net"));
|
|
8
|
-
const DEFAULT_BASE_URL = "https://
|
|
8
|
+
const DEFAULT_BASE_URL = "https://elding.app";
|
|
9
9
|
function stripBrackets(hostname) {
|
|
10
10
|
return hostname.startsWith("[") && hostname.endsWith("]")
|
|
11
11
|
? hostname.slice(1, -1)
|
|
@@ -34,7 +34,7 @@ function resolveBaseUrl(raw = process.env.ELDING_API_URL) {
|
|
|
34
34
|
throw new Error("ELDING_API_URL ne doit pas contenir d'identifiants.");
|
|
35
35
|
}
|
|
36
36
|
if (url.pathname !== "/" || url.search || url.hash) {
|
|
37
|
-
throw new Error("ELDING_API_URL doit etre une origine seule, par exemple https://
|
|
37
|
+
throw new Error("ELDING_API_URL doit etre une origine seule, par exemple https://elding.app.");
|
|
38
38
|
}
|
|
39
39
|
if (url.protocol === "https:")
|
|
40
40
|
return url.origin;
|
package/dist/lib/proxyServer.js
CHANGED
|
@@ -328,6 +328,9 @@ function forwardHttps(req, res, upstream, headers, pinned, hasBody, secretValues
|
|
|
328
328
|
method: req.method,
|
|
329
329
|
headers: { ...headers, host: upstream.host },
|
|
330
330
|
servername: net_1.default.isIP(upstreamHostname) ? undefined : upstreamHostname,
|
|
331
|
+
// Connexion fraiche par requete : evite qu'un socket pinne reutilise
|
|
332
|
+
// soit rejete par le CDN de la cible (faux 401/erreurs intermittentes).
|
|
333
|
+
agent: false,
|
|
331
334
|
timeout: PROXY_TIMEOUT_MS,
|
|
332
335
|
// Node >=20 (autoSelectFamily) appelle lookup avec { all: true } et
|
|
333
336
|
// attend un tableau ; sinon la forme simple (address, family).
|