@elding/cli 0.3.0 → 0.8.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/dist/commands/doctor.js +4 -3
- package/dist/commands/init.js +30 -7
- package/dist/commands/keys.js +4 -3
- package/dist/commands/login.js +51 -25
- package/dist/commands/proxy.d.ts +6 -1
- package/dist/commands/proxy.js +23 -7
- package/dist/commands/run.d.ts +4 -1
- package/dist/commands/run.js +16 -5
- package/dist/commands/sets.js +4 -2
- package/dist/commands/status.js +3 -2
- package/dist/commands/use.js +13 -5
- package/dist/commands/whoami.js +2 -1
- package/dist/index.js +14 -9
- package/dist/lib/api.d.ts +5 -0
- package/dist/lib/api.js +58 -22
- package/dist/lib/apiUrl.d.ts +1 -0
- package/dist/lib/apiUrl.js +44 -0
- package/dist/lib/config.d.ts +11 -0
- package/dist/lib/config.js +100 -7
- package/dist/lib/env.d.ts +5 -0
- package/dist/lib/env.js +49 -0
- package/dist/lib/keychain.d.ts +3 -0
- package/dist/lib/keychain.js +39 -0
- package/dist/lib/logBatcher.d.ts +5 -0
- package/dist/lib/logBatcher.js +42 -0
- package/dist/lib/proxyServer.d.ts +10 -1
- package/dist/lib/proxyServer.js +272 -58
- package/dist/lib/redact.d.ts +3 -0
- package/dist/lib/redact.js +58 -0
- package/dist/lib/terminal.d.ts +2 -0
- package/dist/lib/terminal.js +15 -0
- package/dist/lib/trust.d.ts +2 -0
- package/dist/lib/trust.js +33 -0
- package/package.json +11 -10
package/dist/commands/doctor.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.doctor = doctor;
|
|
4
4
|
const config_js_1 = require("../lib/config.js");
|
|
5
5
|
const api_js_1 = require("../lib/api.js");
|
|
6
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
6
7
|
async function doctor() {
|
|
7
8
|
const { default: chalk } = await import("chalk");
|
|
8
9
|
const ok = (m) => console.log(`${chalk.green("✓")} ${m}`);
|
|
@@ -15,13 +16,13 @@ async function doctor() {
|
|
|
15
16
|
fail("Non connecté — lancez `elding login`");
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
|
-
ok("Token local présent (
|
|
19
|
+
ok("Token local présent (trousseau OS)");
|
|
19
20
|
// 2. Token valide + utilisateur
|
|
20
21
|
let accessToken;
|
|
21
22
|
try {
|
|
22
23
|
accessToken = await (0, api_js_1.exchangeToken)(config.refreshToken);
|
|
23
24
|
const me = await (0, api_js_1.getMe)(accessToken);
|
|
24
|
-
ok(`Authentifié : ${me.email}`);
|
|
25
|
+
ok(`Authentifié : ${(0, terminal_js_1.safeText)(me.email)}`);
|
|
25
26
|
}
|
|
26
27
|
catch {
|
|
27
28
|
fail("Token expiré ou révoqué — relancez `elding login`");
|
|
@@ -33,7 +34,7 @@ async function doctor() {
|
|
|
33
34
|
warn("Aucun set configuré — lancez `elding init` ou `elding use <set>`");
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
|
-
ok(`Set actif : ${project.setName}`);
|
|
37
|
+
ok(`Set actif : ${(0, terminal_js_1.safeText)(project.setName)}`);
|
|
37
38
|
// 4. Clés + verrouillage host
|
|
38
39
|
try {
|
|
39
40
|
const keys = await (0, api_js_1.listKeys)(accessToken, project.setId);
|
package/dist/commands/init.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.init = init;
|
|
4
4
|
const config_js_1 = require("../lib/config.js");
|
|
5
5
|
const api_js_1 = require("../lib/api.js");
|
|
6
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
6
7
|
async function init() {
|
|
7
8
|
const { default: chalk } = await import("chalk");
|
|
8
9
|
const { default: ora } = await import("ora");
|
|
@@ -31,19 +32,41 @@ async function init() {
|
|
|
31
32
|
process.exit(1);
|
|
32
33
|
}
|
|
33
34
|
if (sets.length === 0) {
|
|
34
|
-
console.log(chalk.yellow("Aucun set trouvé. Créez-en un sur
|
|
35
|
+
console.log(chalk.yellow("Aucun set trouvé. Créez-en un sur le vault."));
|
|
35
36
|
process.exit(0);
|
|
36
37
|
}
|
|
38
|
+
// Étape 1 : choisir l'organisation (toujours, pour une approche projet cohérente)
|
|
39
|
+
const orgs = [...new Map(sets.map((s) => [s.workspaceId, s.workspaceName])).entries()];
|
|
40
|
+
const { workspaceId } = await inquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
type: "select",
|
|
43
|
+
name: "workspaceId",
|
|
44
|
+
message: "Quelle organisation ?",
|
|
45
|
+
choices: orgs.map(([id, name]) => ({ name: (0, terminal_js_1.safeText)(name), value: id })),
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
// Étape 2 : choisir le set parmi ceux de l'org (env affiché en français)
|
|
49
|
+
const ENV_LABELS = { DEV: "Dev", STAGING: "Staging", PROD: "Prod" };
|
|
50
|
+
const orgSets = sets.filter((s) => s.workspaceId === workspaceId);
|
|
37
51
|
const { setId } = await inquirer.prompt([
|
|
38
52
|
{
|
|
39
|
-
type: "
|
|
53
|
+
type: "select",
|
|
40
54
|
name: "setId",
|
|
41
55
|
message: "Quel set utiliser pour ce projet ?",
|
|
42
|
-
choices:
|
|
56
|
+
choices: orgSets.map((s) => {
|
|
57
|
+
const env = s.environment ? ENV_LABELS[s.environment] : undefined;
|
|
58
|
+
return { name: env ? `${(0, terminal_js_1.safeText)(s.name)} (${env})` : (0, terminal_js_1.safeText)(s.name), value: s.id };
|
|
59
|
+
}),
|
|
43
60
|
},
|
|
44
61
|
]);
|
|
45
|
-
const selected =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
const selected = orgSets.find((s) => s.id === setId);
|
|
63
|
+
const project = {
|
|
64
|
+
setId: selected.id,
|
|
65
|
+
setName: selected.name,
|
|
66
|
+
workspaceId: selected.workspaceId,
|
|
67
|
+
workspaceName: selected.workspaceName,
|
|
68
|
+
};
|
|
69
|
+
(0, config_js_1.writeProject)(project);
|
|
70
|
+
(0, config_js_1.trustProject)(project);
|
|
71
|
+
console.log(chalk.green(`✓ Set "${(0, terminal_js_1.safeText)(selected.name)}" (${(0, terminal_js_1.safeText)(selected.workspaceName)}) configuré dans .elding.json`));
|
|
49
72
|
}
|
package/dist/commands/keys.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.keys = keys;
|
|
|
4
4
|
const api_js_1 = require("../lib/api.js");
|
|
5
5
|
const config_js_1 = require("../lib/config.js");
|
|
6
6
|
const session_js_1 = require("../lib/session.js");
|
|
7
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
7
8
|
async function keys() {
|
|
8
9
|
const { default: chalk } = await import("chalk");
|
|
9
10
|
const { default: ora } = await import("ora");
|
|
@@ -16,15 +17,15 @@ async function keys() {
|
|
|
16
17
|
const accessToken = await (0, session_js_1.requireAccessToken)();
|
|
17
18
|
const list = await (0, api_js_1.listKeys)(accessToken, project.setId);
|
|
18
19
|
spinner.stop();
|
|
19
|
-
console.log(chalk.dim(`Set : ${project.setName}`));
|
|
20
|
+
console.log(chalk.dim(`Set : ${(0, terminal_js_1.safeText)(project.setName)}`));
|
|
20
21
|
if (list.length === 0) {
|
|
21
22
|
console.log(chalk.yellow("Aucune clé dans ce set."));
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
25
|
for (const k of list) {
|
|
25
26
|
const host = k.allowedHost
|
|
26
|
-
? chalk.dim(` → ${k.allowedHost}`)
|
|
27
|
+
? chalk.dim(` → ${(0, terminal_js_1.safeText)(k.allowedHost)}`)
|
|
27
28
|
: chalk.yellow(" → aucun domaine (non verrouillé)");
|
|
28
|
-
console.log(` ${k.name}${host}`);
|
|
29
|
+
console.log(` ${(0, terminal_js_1.safeText)(k.name)}${host}`);
|
|
29
30
|
}
|
|
30
31
|
}
|
package/dist/commands/login.js
CHANGED
|
@@ -7,52 +7,78 @@ exports.login = login;
|
|
|
7
7
|
const http_1 = __importDefault(require("http"));
|
|
8
8
|
const crypto_1 = __importDefault(require("crypto"));
|
|
9
9
|
const config_js_1 = require("../lib/config.js");
|
|
10
|
-
const
|
|
10
|
+
const api_js_1 = require("../lib/api.js");
|
|
11
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
11
12
|
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
12
|
-
function randomPort() {
|
|
13
|
-
return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152;
|
|
14
|
-
}
|
|
15
13
|
async function login() {
|
|
16
14
|
const { default: chalk } = await import("chalk");
|
|
17
15
|
const { default: open } = await import("open");
|
|
18
16
|
const { default: ora } = await import("ora");
|
|
19
17
|
const state = crypto_1.default.randomBytes(16).toString("hex");
|
|
20
|
-
const port = randomPort();
|
|
21
|
-
const callbackUrl = `http://localhost:${port}`;
|
|
22
|
-
const authUrl = `${BASE_URL}/cli` +
|
|
23
|
-
`?state=${encodeURIComponent(state)}` +
|
|
24
|
-
`&callback=${encodeURIComponent(callbackUrl)}`;
|
|
25
18
|
const spinner = ora("En attente d'autorisation dans le navigateur...").start();
|
|
26
19
|
const token = await new Promise((resolve, reject) => {
|
|
27
20
|
const timeout = setTimeout(() => {
|
|
28
21
|
server.close();
|
|
29
22
|
reject(new Error("Timeout — aucune réponse après 5 minutes"));
|
|
30
23
|
}, TIMEOUT_MS);
|
|
31
|
-
const server = http_1.default.createServer((req, res) => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
24
|
+
const server = http_1.default.createServer(async (req, res) => {
|
|
25
|
+
const addr = server.address();
|
|
26
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
27
|
+
const callbackUrl = `http://127.0.0.1:${port}`;
|
|
28
|
+
const url = new URL(req.url ?? "/", callbackUrl);
|
|
29
|
+
const receivedCode = url.searchParams.get("code");
|
|
34
30
|
const receivedState = url.searchParams.get("state");
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (receivedState !== state || !tokenValid) {
|
|
31
|
+
const codeValid = typeof receivedCode === "string" &&
|
|
32
|
+
/^[A-Za-z0-9._~-]{16,512}$/.test(receivedCode);
|
|
33
|
+
if (receivedState !== state || !codeValid) {
|
|
39
34
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
40
35
|
res.end("<p>Paramètres invalides. Fermez cet onglet.</p>");
|
|
41
36
|
return;
|
|
42
37
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
try {
|
|
39
|
+
const refreshToken = await (0, api_js_1.exchangeLoginCode)(receivedCode, state, callbackUrl);
|
|
40
|
+
res.writeHead(200, {
|
|
41
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
42
|
+
"Referrer-Policy": "no-referrer",
|
|
43
|
+
"Cache-Control": "no-store",
|
|
44
|
+
});
|
|
45
|
+
res.end(`<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><title>Elding</title><script>history.replaceState(null,"","/")</script><style>
|
|
46
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
47
|
+
html,body{height:100%}
|
|
48
|
+
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#fff;color:#000;display:flex;align-items:center;justify-content:center;text-align:center}
|
|
49
|
+
.wrap{display:flex;flex-direction:column;align-items:center;gap:.75rem;padding:2rem}
|
|
50
|
+
h1{font-size:2.25rem;font-weight:600;letter-spacing:-.02em}
|
|
51
|
+
p{font-size:1rem;color:#6b7280}
|
|
52
|
+
</style></head><body>
|
|
53
|
+
<div class="wrap">
|
|
54
|
+
<h1>Tout est prêt, codez l'esprit tranquille</h1>
|
|
55
|
+
<p>Vous êtes connecté à Elding.</p>
|
|
56
|
+
<p>Vous pouvez maintenant fermer cette fenêtre.</p>
|
|
57
|
+
</div>
|
|
58
|
+
</body></html>`);
|
|
59
|
+
clearTimeout(timeout);
|
|
60
|
+
server.close();
|
|
61
|
+
resolve(refreshToken);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
res.writeHead(502, { "Content-Type": "text/html; charset=utf-8" });
|
|
65
|
+
res.end("<p>Impossible de finaliser l'authentification. Fermez cet onglet et relancez le login.</p>");
|
|
66
|
+
clearTimeout(timeout);
|
|
67
|
+
server.close();
|
|
68
|
+
reject(new Error((0, terminal_js_1.safeError)(err)));
|
|
69
|
+
}
|
|
50
70
|
});
|
|
51
71
|
server.on("error", (err) => {
|
|
52
72
|
clearTimeout(timeout);
|
|
53
73
|
reject(err);
|
|
54
74
|
});
|
|
55
|
-
server.listen(
|
|
75
|
+
server.listen(0, "127.0.0.1", () => {
|
|
76
|
+
const addr = server.address();
|
|
77
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
78
|
+
const callbackUrl = `http://127.0.0.1:${port}`;
|
|
79
|
+
const authUrl = `${api_js_1.BASE_URL}/cli` +
|
|
80
|
+
`?state=${encodeURIComponent(state)}` +
|
|
81
|
+
`&callback=${encodeURIComponent(callbackUrl)}`;
|
|
56
82
|
open(authUrl).catch((err) => {
|
|
57
83
|
spinner.warn(`Impossible d'ouvrir le navigateur automatiquement.`);
|
|
58
84
|
console.log(chalk.cyan(`Ouvrez manuellement : ${authUrl}`));
|
|
@@ -61,5 +87,5 @@ async function login() {
|
|
|
61
87
|
});
|
|
62
88
|
(0, config_js_1.writeConfig)({ refreshToken: token });
|
|
63
89
|
spinner.succeed(chalk.green("Connecté avec succès."));
|
|
64
|
-
console.log(chalk.dim("Token sauvegardé dans
|
|
90
|
+
console.log(chalk.dim("Token sauvegardé dans le trousseau de votre OS."));
|
|
65
91
|
}
|
package/dist/commands/proxy.d.ts
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type ProxyOptions = {
|
|
2
|
+
verbose?: boolean;
|
|
3
|
+
reportLogs?: boolean;
|
|
4
|
+
shell?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function proxy(cmd: string, args: string[], options?: ProxyOptions): Promise<void>;
|
package/dist/commands/proxy.js
CHANGED
|
@@ -5,7 +5,10 @@ const child_process_1 = require("child_process");
|
|
|
5
5
|
const config_js_1 = require("../lib/config.js");
|
|
6
6
|
const api_js_1 = require("../lib/api.js");
|
|
7
7
|
const proxyServer_js_1 = require("../lib/proxyServer.js");
|
|
8
|
-
|
|
8
|
+
const logBatcher_js_1 = require("../lib/logBatcher.js");
|
|
9
|
+
const trust_js_1 = require("../lib/trust.js");
|
|
10
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
11
|
+
async function proxy(cmd, args, options = {}) {
|
|
9
12
|
const { default: chalk } = await import("chalk");
|
|
10
13
|
const { default: ora } = await import("ora");
|
|
11
14
|
const config = (0, config_js_1.readConfig)();
|
|
@@ -18,6 +21,10 @@ async function proxy(cmd, args, verbose = false) {
|
|
|
18
21
|
console.error(chalk.red("Projet non initialisé. Lancez `elding init` d'abord."));
|
|
19
22
|
process.exit(1);
|
|
20
23
|
}
|
|
24
|
+
await (0, trust_js_1.ensureProjectTrusted)(project);
|
|
25
|
+
if (!options.shell && /\s/.test(cmd)) {
|
|
26
|
+
throw new Error("Commande invalide sans --shell. Passez le binaire et ses arguments separement, ou ajoutez --shell explicitement.");
|
|
27
|
+
}
|
|
21
28
|
const spinner = ora("Démarrage du proxy...").start();
|
|
22
29
|
let secrets;
|
|
23
30
|
let hosts;
|
|
@@ -26,12 +33,17 @@ async function proxy(cmd, args, verbose = false) {
|
|
|
26
33
|
({ secrets, hosts } = await (0, api_js_1.fetchSecrets)(accessToken, project.setId));
|
|
27
34
|
}
|
|
28
35
|
catch (err) {
|
|
29
|
-
spinner.fail(chalk.red(
|
|
36
|
+
spinner.fail(chalk.red((0, terminal_js_1.safeError)(err)));
|
|
30
37
|
process.exit(1);
|
|
31
38
|
}
|
|
32
|
-
const
|
|
33
|
-
|
|
39
|
+
const batcher = options.reportLogs
|
|
40
|
+
? (0, logBatcher_js_1.createLogBatcher)(config.refreshToken, project.setId, project.setName)
|
|
41
|
+
: null;
|
|
42
|
+
const server = await (0, proxyServer_js_1.startProxy)(secrets, hosts, !!options.verbose, batcher ? (e) => batcher.add(e) : undefined);
|
|
43
|
+
spinner.succeed(chalk.green(`Proxy actif sur ${server.url} — ${Object.keys(secrets).length} secret(s) pour ${(0, terminal_js_1.safeText)(project.setName)}`));
|
|
34
44
|
console.log(chalk.dim("Les clés restent dans le proxy, jamais dans la mémoire de l'app."));
|
|
45
|
+
if (!options.reportLogs)
|
|
46
|
+
console.log(chalk.dim("Logs cloud proxy désactivés. Ajoutez --report-logs pour les envoyer."));
|
|
35
47
|
const child = (0, child_process_1.spawn)(cmd, args, {
|
|
36
48
|
env: {
|
|
37
49
|
...process.env,
|
|
@@ -39,13 +51,17 @@ async function proxy(cmd, args, verbose = false) {
|
|
|
39
51
|
ELDING_PROXY_TOKEN: server.token,
|
|
40
52
|
},
|
|
41
53
|
stdio: "inherit",
|
|
42
|
-
shell:
|
|
54
|
+
shell: !!options.shell,
|
|
43
55
|
});
|
|
44
|
-
const shutdown = () =>
|
|
56
|
+
const shutdown = async () => {
|
|
57
|
+
server.close();
|
|
58
|
+
await batcher?.stop();
|
|
59
|
+
};
|
|
45
60
|
process.on("SIGINT", shutdown);
|
|
46
61
|
process.on("SIGTERM", shutdown);
|
|
47
|
-
child.on("exit", (code) => {
|
|
62
|
+
child.on("exit", async (code) => {
|
|
48
63
|
server.close();
|
|
64
|
+
await batcher?.stop(); // flush les derniers logs
|
|
49
65
|
process.exit(code ?? 0);
|
|
50
66
|
});
|
|
51
67
|
}
|
package/dist/commands/run.d.ts
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -4,7 +4,10 @@ exports.run = run;
|
|
|
4
4
|
const child_process_1 = require("child_process");
|
|
5
5
|
const config_js_1 = require("../lib/config.js");
|
|
6
6
|
const api_js_1 = require("../lib/api.js");
|
|
7
|
-
|
|
7
|
+
const env_js_1 = require("../lib/env.js");
|
|
8
|
+
const trust_js_1 = require("../lib/trust.js");
|
|
9
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
10
|
+
async function run(cmd, args, options = {}) {
|
|
8
11
|
const { default: chalk } = await import("chalk");
|
|
9
12
|
const { default: ora } = await import("ora");
|
|
10
13
|
const config = (0, config_js_1.readConfig)();
|
|
@@ -17,22 +20,30 @@ async function run(cmd, args) {
|
|
|
17
20
|
console.error(chalk.red("Projet non initialisé. Lancez `elding init` d'abord."));
|
|
18
21
|
process.exit(1);
|
|
19
22
|
}
|
|
23
|
+
await (0, trust_js_1.ensureProjectTrusted)(project);
|
|
24
|
+
if (!options.shell && /\s/.test(cmd)) {
|
|
25
|
+
throw new Error("Commande invalide sans --shell. Passez le binaire et ses arguments separement, ou ajoutez --shell explicitement.");
|
|
26
|
+
}
|
|
20
27
|
const spinner = ora("Récupération des secrets...").start();
|
|
21
28
|
let secrets;
|
|
22
29
|
try {
|
|
23
30
|
const accessToken = await (0, api_js_1.exchangeToken)(config.refreshToken);
|
|
24
31
|
({ secrets } = await (0, api_js_1.fetchSecrets)(accessToken, project.setId, "run"));
|
|
25
|
-
spinner.succeed(chalk.green(`${Object.keys(secrets).length} secret(s) chargé(s).`));
|
|
32
|
+
spinner.succeed(chalk.green(`${Object.keys(secrets).length} secret(s) chargé(s) pour ${(0, terminal_js_1.safeText)(project.setName)}.`));
|
|
26
33
|
}
|
|
27
34
|
catch (err) {
|
|
28
|
-
spinner.fail(chalk.red(
|
|
35
|
+
spinner.fail(chalk.red((0, terminal_js_1.safeError)(err)));
|
|
29
36
|
process.exit(1);
|
|
30
37
|
}
|
|
31
|
-
const env =
|
|
38
|
+
const { env: safeSecrets, rejected } = (0, env_js_1.filterSecretsForEnv)(secrets);
|
|
39
|
+
if (rejected.length > 0) {
|
|
40
|
+
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
|
+
}
|
|
42
|
+
const env = { ...process.env, ...safeSecrets };
|
|
32
43
|
const result = (0, child_process_1.spawnSync)(cmd, args, {
|
|
33
44
|
env,
|
|
34
45
|
stdio: "inherit",
|
|
35
|
-
shell:
|
|
46
|
+
shell: !!options.shell,
|
|
36
47
|
});
|
|
37
48
|
if (result.error) {
|
|
38
49
|
console.error(chalk.red(`Impossible de lancer la commande : ${result.error.message}`));
|
package/dist/commands/sets.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.sets = sets;
|
|
|
4
4
|
const api_js_1 = require("../lib/api.js");
|
|
5
5
|
const config_js_1 = require("../lib/config.js");
|
|
6
6
|
const session_js_1 = require("../lib/session.js");
|
|
7
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
7
8
|
async function sets() {
|
|
8
9
|
const { default: chalk } = await import("chalk");
|
|
9
10
|
const { default: ora } = await import("ora");
|
|
@@ -18,7 +19,8 @@ async function sets() {
|
|
|
18
19
|
const activeId = (0, config_js_1.readProject)()?.setId;
|
|
19
20
|
for (const s of list) {
|
|
20
21
|
const marker = s.id === activeId ? chalk.green("●") : chalk.dim("○");
|
|
21
|
-
const
|
|
22
|
-
|
|
22
|
+
const cleanName = (0, terminal_js_1.safeText)(s.name);
|
|
23
|
+
const name = s.id === activeId ? chalk.green(cleanName) : cleanName;
|
|
24
|
+
console.log(`${marker} ${name} ${chalk.dim(`(${(0, terminal_js_1.safeText)(s.workspaceName)})`)}`);
|
|
23
25
|
}
|
|
24
26
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.status = status;
|
|
4
4
|
const config_js_1 = require("../lib/config.js");
|
|
5
5
|
const api_js_1 = require("../lib/api.js");
|
|
6
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
6
7
|
async function status() {
|
|
7
8
|
const { default: chalk } = await import("chalk");
|
|
8
9
|
const config = (0, config_js_1.readConfig)();
|
|
@@ -23,7 +24,7 @@ async function status() {
|
|
|
23
24
|
const project = (0, config_js_1.readProject)();
|
|
24
25
|
const proxyActive = !!process.env.ELDING_PROXY_URL;
|
|
25
26
|
console.log(chalk.green("● Connecté"));
|
|
26
|
-
console.log(` ${chalk.dim("Utilisateur")} ${user.email}${user.name ? chalk.dim(` (${user.name})`) : ""}`);
|
|
27
|
-
console.log(` ${chalk.dim("Set actif")} ${project ? `${project.setName} ${chalk.dim(`(${project.setId})`)}` : chalk.yellow("aucun — `elding init`")}`);
|
|
27
|
+
console.log(` ${chalk.dim("Utilisateur")} ${(0, terminal_js_1.safeText)(user.email)}${user.name ? chalk.dim(` (${(0, terminal_js_1.safeText)(user.name)})`) : ""}`);
|
|
28
|
+
console.log(` ${chalk.dim("Set actif")} ${project ? `${(0, terminal_js_1.safeText)(project.setName)} ${chalk.dim(`(${(0, terminal_js_1.safeText)(project.setId)})`)}` : chalk.yellow("aucun — `elding init`")}`);
|
|
28
29
|
console.log(` ${chalk.dim("Proxy")} ${proxyActive ? chalk.green("actif") : chalk.dim("inactif")}`);
|
|
29
30
|
}
|
package/dist/commands/use.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.use = use;
|
|
|
4
4
|
const api_js_1 = require("../lib/api.js");
|
|
5
5
|
const config_js_1 = require("../lib/config.js");
|
|
6
6
|
const session_js_1 = require("../lib/session.js");
|
|
7
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
7
8
|
async function use(nameArg) {
|
|
8
9
|
const { default: chalk } = await import("chalk");
|
|
9
10
|
const { default: ora } = await import("ora");
|
|
@@ -15,15 +16,22 @@ async function use(nameArg) {
|
|
|
15
16
|
const matches = list.filter((s) => s.name.toLowerCase() === query);
|
|
16
17
|
const candidates = matches.length ? matches : list.filter((s) => s.name.toLowerCase().includes(query));
|
|
17
18
|
if (candidates.length === 0) {
|
|
18
|
-
console.error(chalk.red(`Aucun set nommé "${nameArg}".`) + chalk.dim(" Voir `elding sets`."));
|
|
19
|
+
console.error(chalk.red(`Aucun set nommé "${(0, terminal_js_1.safeText)(nameArg)}".`) + chalk.dim(" Voir `elding sets`."));
|
|
19
20
|
process.exit(1);
|
|
20
21
|
}
|
|
21
22
|
if (candidates.length > 1) {
|
|
22
|
-
console.error(chalk.red(`Plusieurs sets correspondent à "${nameArg}" :`));
|
|
23
|
-
candidates.forEach((s) => console.error(chalk.dim(` - ${s.name}`)));
|
|
23
|
+
console.error(chalk.red(`Plusieurs sets correspondent à "${(0, terminal_js_1.safeText)(nameArg)}" :`));
|
|
24
|
+
candidates.forEach((s) => console.error(chalk.dim(` - ${(0, terminal_js_1.safeText)(s.name)}`)));
|
|
24
25
|
process.exit(1);
|
|
25
26
|
}
|
|
26
27
|
const set = candidates[0];
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const project = {
|
|
29
|
+
setId: set.id,
|
|
30
|
+
setName: set.name,
|
|
31
|
+
workspaceId: set.workspaceId,
|
|
32
|
+
workspaceName: set.workspaceName,
|
|
33
|
+
};
|
|
34
|
+
(0, config_js_1.writeProject)(project);
|
|
35
|
+
(0, config_js_1.trustProject)(project);
|
|
36
|
+
console.log(chalk.green(`✓ Set actif : "${(0, terminal_js_1.safeText)(set.name)}"`));
|
|
29
37
|
}
|
package/dist/commands/whoami.js
CHANGED
|
@@ -3,9 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.whoami = whoami;
|
|
4
4
|
const api_js_1 = require("../lib/api.js");
|
|
5
5
|
const session_js_1 = require("../lib/session.js");
|
|
6
|
+
const terminal_js_1 = require("../lib/terminal.js");
|
|
6
7
|
async function whoami() {
|
|
7
8
|
const { default: chalk } = await import("chalk");
|
|
8
9
|
const accessToken = await (0, session_js_1.requireAccessToken)();
|
|
9
10
|
const me = await (0, api_js_1.getMe)(accessToken);
|
|
10
|
-
console.log(`${me.email}${me.name ? chalk.dim(` (${me.name})`) : ""}`);
|
|
11
|
+
console.log(`${(0, terminal_js_1.safeText)(me.email)}${me.name ? chalk.dim(` (${(0, terminal_js_1.safeText)(me.name)})`) : ""}`);
|
|
11
12
|
}
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ 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.1
|
|
21
|
+
.version("0.8.1");
|
|
22
22
|
program
|
|
23
23
|
.command("login")
|
|
24
24
|
.description("Authentifier le CLI via le navigateur")
|
|
@@ -38,24 +38,29 @@ program
|
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
program
|
|
41
|
-
.command("run <cmd>")
|
|
41
|
+
.command("run <cmd> [args...]")
|
|
42
42
|
.description("Lancer une commande avec les secrets injectés en variables d'environnement")
|
|
43
|
+
.option("--shell", "Executer via le shell systeme (a utiliser seulement si necessaire)")
|
|
43
44
|
.allowUnknownOption()
|
|
44
|
-
.action(async (cmd,
|
|
45
|
-
|
|
46
|
-
await (0, run_js_1.run)(cmd, args).catch((err) => {
|
|
45
|
+
.action(async (cmd, args = [], options) => {
|
|
46
|
+
await (0, run_js_1.run)(cmd, args, { shell: !!options.shell }).catch((err) => {
|
|
47
47
|
console.error(err.message);
|
|
48
48
|
process.exit(1);
|
|
49
49
|
});
|
|
50
50
|
});
|
|
51
51
|
program
|
|
52
|
-
.command("proxy <cmd>")
|
|
52
|
+
.command("proxy <cmd> [args...]")
|
|
53
53
|
.description("Lancer une commande derrière un proxy local qui injecte les clés — jamais en mémoire de l'app")
|
|
54
54
|
.option("-v, --verbose", "Logger chaque requête (méthode/host/status/latence)")
|
|
55
|
+
.option("--report-logs", "Envoyer les metadonnees de requetes proxy au vault")
|
|
56
|
+
.option("--shell", "Executer via le shell systeme (a utiliser seulement si necessaire)")
|
|
55
57
|
.allowUnknownOption()
|
|
56
|
-
.action(async (cmd,
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
.action(async (cmd, args = [], options) => {
|
|
59
|
+
await (0, proxy_js_1.proxy)(cmd, args, {
|
|
60
|
+
verbose: !!options.verbose,
|
|
61
|
+
reportLogs: !!options.reportLogs,
|
|
62
|
+
shell: !!options.shell,
|
|
63
|
+
}).catch((err) => {
|
|
59
64
|
console.error(err.message);
|
|
60
65
|
process.exit(1);
|
|
61
66
|
});
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export declare const BASE_URL: string;
|
|
|
2
2
|
export type ApiKeySetItem = {
|
|
3
3
|
id: string;
|
|
4
4
|
name: string;
|
|
5
|
+
environment?: string;
|
|
6
|
+
workspaceId: string;
|
|
7
|
+
workspaceName: string;
|
|
5
8
|
};
|
|
6
9
|
export type KeyItem = {
|
|
7
10
|
name: string;
|
|
@@ -9,6 +12,8 @@ export type KeyItem = {
|
|
|
9
12
|
};
|
|
10
13
|
export declare function listKeys(accessToken: string, setId: string): Promise<KeyItem[]>;
|
|
11
14
|
export declare function exchangeToken(refreshToken: string): Promise<string>;
|
|
15
|
+
export declare function exchangeLoginCode(code: string, state: string, callbackUrl: string): Promise<string>;
|
|
16
|
+
export declare function reportProxyLogs(accessToken: string, setId: string, setName: string, entries: unknown[]): Promise<void>;
|
|
12
17
|
export declare function revokeToken(refreshToken: string): Promise<void>;
|
|
13
18
|
export declare function getMe(accessToken: string): Promise<{
|
|
14
19
|
email: string;
|
package/dist/lib/api.js
CHANGED
|
@@ -3,65 +3,86 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BASE_URL = void 0;
|
|
4
4
|
exports.listKeys = listKeys;
|
|
5
5
|
exports.exchangeToken = exchangeToken;
|
|
6
|
+
exports.exchangeLoginCode = exchangeLoginCode;
|
|
7
|
+
exports.reportProxyLogs = reportProxyLogs;
|
|
6
8
|
exports.revokeToken = revokeToken;
|
|
7
9
|
exports.getMe = getMe;
|
|
8
10
|
exports.listSets = listSets;
|
|
9
11
|
exports.fetchSecrets = fetchSecrets;
|
|
10
|
-
|
|
12
|
+
const apiUrl_js_1 = require("./apiUrl.js");
|
|
13
|
+
const terminal_js_1 = require("./terminal.js");
|
|
14
|
+
exports.BASE_URL = (0, apiUrl_js_1.resolveBaseUrl)();
|
|
15
|
+
const REQUEST_TIMEOUT_MS = 15_000;
|
|
16
|
+
const LOG_TIMEOUT_MS = 5_000;
|
|
17
|
+
const REFRESH_TOKEN = /^eld_rt_[a-f0-9]{64}$/i;
|
|
18
|
+
const LOGIN_CODE = /^[A-Za-z0-9._~-]{16,512}$/;
|
|
11
19
|
async function listKeys(accessToken, setId) {
|
|
12
|
-
const
|
|
20
|
+
const body = await requestJson(`/api/cli/keys?setId=${encodeURIComponent(setId)}`, {
|
|
13
21
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
14
22
|
});
|
|
15
|
-
if (!res.ok)
|
|
16
|
-
throw new Error("Impossible de récupérer les clés");
|
|
17
|
-
const body = (await res.json());
|
|
18
23
|
return Array.isArray(body.keys) ? body.keys : [];
|
|
19
24
|
}
|
|
20
25
|
async function exchangeToken(refreshToken) {
|
|
21
|
-
|
|
26
|
+
if (!REFRESH_TOKEN.test(refreshToken))
|
|
27
|
+
throw new Error("Token local invalide. Relancez `elding login`.");
|
|
28
|
+
const body = await requestJson("/api/cli/auth/token", {
|
|
22
29
|
method: "POST",
|
|
23
30
|
headers: { "Content-Type": "application/json" },
|
|
24
31
|
body: JSON.stringify({ refreshToken }),
|
|
25
32
|
});
|
|
26
|
-
const body = (await res.json());
|
|
27
33
|
if (!body.success || !body.accessToken)
|
|
28
|
-
throw new Error(body.error
|
|
34
|
+
throw new Error((0, terminal_js_1.safeText)(body.error, 200) || "Impossible d'obtenir un access token");
|
|
29
35
|
return body.accessToken;
|
|
30
36
|
}
|
|
37
|
+
async function exchangeLoginCode(code, state, callbackUrl) {
|
|
38
|
+
if (!LOGIN_CODE.test(code))
|
|
39
|
+
throw new Error("Code d'autorisation invalide.");
|
|
40
|
+
const body = await requestJson("/api/cli/auth/exchange", {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "Content-Type": "application/json" },
|
|
43
|
+
body: JSON.stringify({ code, state, callbackUrl }),
|
|
44
|
+
});
|
|
45
|
+
if (!body.success || !body.refreshToken || !REFRESH_TOKEN.test(body.refreshToken)) {
|
|
46
|
+
throw new Error((0, terminal_js_1.safeText)(body.error, 200) || "Impossible de finaliser l'authentification.");
|
|
47
|
+
}
|
|
48
|
+
return body.refreshToken;
|
|
49
|
+
}
|
|
50
|
+
async function reportProxyLogs(accessToken, setId, setName, entries) {
|
|
51
|
+
await fetch(`${exports.BASE_URL}/api/cli/proxy-logs`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}` },
|
|
54
|
+
body: JSON.stringify({ setId, setName, entries }),
|
|
55
|
+
signal: AbortSignal.timeout(LOG_TIMEOUT_MS),
|
|
56
|
+
}).catch(() => { });
|
|
57
|
+
}
|
|
31
58
|
async function revokeToken(refreshToken) {
|
|
59
|
+
if (!REFRESH_TOKEN.test(refreshToken))
|
|
60
|
+
return;
|
|
32
61
|
await fetch(`${exports.BASE_URL}/api/cli/auth/logout`, {
|
|
33
62
|
method: "POST",
|
|
34
63
|
headers: { "Content-Type": "application/json" },
|
|
35
64
|
body: JSON.stringify({ refreshToken }),
|
|
65
|
+
signal: AbortSignal.timeout(LOG_TIMEOUT_MS),
|
|
36
66
|
}).catch(() => { });
|
|
37
67
|
}
|
|
38
68
|
async function getMe(accessToken) {
|
|
39
|
-
const
|
|
69
|
+
const body = await requestJson("/api/cli/me", {
|
|
40
70
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
41
71
|
});
|
|
42
|
-
if (!res.ok)
|
|
43
|
-
throw new Error("Impossible de récupérer l'utilisateur");
|
|
44
|
-
const body = (await res.json());
|
|
45
72
|
if (!body.success || !body.user)
|
|
46
73
|
throw new Error("Réponse invalide");
|
|
47
74
|
return body.user;
|
|
48
75
|
}
|
|
49
76
|
async function listSets(accessToken) {
|
|
50
|
-
const
|
|
77
|
+
const body = await requestJson("/api/cli/sets", {
|
|
51
78
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
52
79
|
});
|
|
53
|
-
if (!res.ok)
|
|
54
|
-
throw new Error("Impossible de récupérer les sets");
|
|
55
|
-
const body = (await res.json());
|
|
56
80
|
return Array.isArray(body.sets) ? body.sets : [];
|
|
57
81
|
}
|
|
58
82
|
async function fetchSecrets(accessToken, setId, mode = "proxy") {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
throw new Error(body.error ?? `Erreur ${res.status}`);
|
|
63
|
-
}
|
|
64
|
-
const body = (await res.json());
|
|
83
|
+
const body = await requestJson(`/api/cli/secrets?setId=${encodeURIComponent(setId)}&mode=${mode}`, {
|
|
84
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
85
|
+
});
|
|
65
86
|
if (!body.success || !body.secrets || typeof body.secrets !== "object")
|
|
66
87
|
throw new Error("Réponse invalide");
|
|
67
88
|
return {
|
|
@@ -76,9 +97,24 @@ function sanitizeSecrets(raw) {
|
|
|
76
97
|
for (const [name, value] of Object.entries(raw)) {
|
|
77
98
|
if (DANGEROUS_KEYS.has(name))
|
|
78
99
|
continue;
|
|
100
|
+
if (name.length > 128)
|
|
101
|
+
continue;
|
|
79
102
|
if (typeof value !== "string")
|
|
80
103
|
continue;
|
|
81
104
|
out[name] = value;
|
|
82
105
|
}
|
|
83
106
|
return out;
|
|
84
107
|
}
|
|
108
|
+
async function requestJson(path, init) {
|
|
109
|
+
const res = await fetch(`${exports.BASE_URL}${path}`, {
|
|
110
|
+
...init,
|
|
111
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
112
|
+
});
|
|
113
|
+
const body = (await res.json().catch(() => null));
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
throw new Error((0, terminal_js_1.safeText)(body?.error, 200) || `Erreur ${res.status}`);
|
|
116
|
+
}
|
|
117
|
+
if (!body || typeof body !== "object")
|
|
118
|
+
throw new Error("Réponse invalide");
|
|
119
|
+
return body;
|
|
120
|
+
}
|