@etree/cli 2.0.1 → 2.0.3

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.
@@ -1,23 +1,8 @@
1
- /**
2
- * Generate a new X25519 key pair.
3
- */
4
1
  export declare function generateKeys(): Promise<{
5
2
  private_key: string;
6
3
  public_key: string;
7
4
  }>;
8
- /**
9
- * Derive the public key from an existing private key.
10
- */
11
5
  export declare function derivePublicKey(privateKey: string): Promise<string>;
12
- /**
13
- * Encrypt a plaintext value using the recipient's public key (SealedBox).
14
- */
15
6
  export declare function encrypt(plaintext: string, publicKey: string): Promise<string>;
16
- /**
17
- * Decrypt a ciphertext using the recipient's private key (SealedBox).
18
- */
19
7
  export declare function decrypt(ciphertext: string, privateKey: string): Promise<string>;
20
- /**
21
- * Shut down the crypto engine process.
22
- */
23
8
  export declare function shutdown(): void;
@@ -1,173 +1,49 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
35
5
  Object.defineProperty(exports, "__esModule", { value: true });
36
6
  exports.generateKeys = generateKeys;
37
7
  exports.derivePublicKey = derivePublicKey;
38
8
  exports.encrypt = encrypt;
39
9
  exports.decrypt = decrypt;
40
10
  exports.shutdown = shutdown;
41
- const child_process_1 = require("child_process");
42
- const path = __importStar(require("path"));
43
- const CRYPTO_ENGINE_PATH = path.resolve(__dirname, "../../../../packages/crypto/engine.py");
44
- let engineProcess = null;
45
- /**
46
- * Get or spawn the crypto engine Python process.
47
- */
48
- function getEngine() {
49
- if (engineProcess && !engineProcess.killed) {
50
- return engineProcess;
51
- }
52
- // Try venv python first, fall back to system python
53
- const venvDir = path.resolve(__dirname, "../../../../packages/crypto/.venv");
54
- const isWindows = process.platform === "win32";
55
- // Potential Venv Paths
56
- const potentialVenvPaths = isWindows
57
- ? [
58
- path.join(venvDir, "Scripts", "python.exe"),
59
- path.join(venvDir, "bin", "python.exe"),
60
- ]
61
- : [path.join(venvDir, "bin", "python")];
62
- let pythonCmd = isWindows ? "python" : "python3";
63
- for (const venvPath of potentialVenvPaths) {
64
- if (require("fs").existsSync(venvPath)) {
65
- pythonCmd = venvPath;
66
- break;
67
- }
68
- }
69
- // If we haven't found a venv python, verify the system command exists
70
- if (pythonCmd === "python" || pythonCmd === "python3") {
71
- try {
72
- // Use system python if it exists and works
73
- require("child_process").execSync(`${pythonCmd} --version`, {
74
- stdio: "ignore",
75
- });
76
- }
77
- catch {
78
- // If the preferred one fails, try the fallback
79
- pythonCmd = pythonCmd === "python3" ? "python" : "python3";
80
- }
81
- }
82
- engineProcess = (0, child_process_1.spawn)(pythonCmd, [CRYPTO_ENGINE_PATH], {
83
- stdio: ["pipe", "pipe", "pipe"],
84
- cwd: path.resolve(__dirname, "../../../../packages/crypto"),
85
- });
86
- engineProcess.on("exit", () => {
87
- engineProcess = null;
88
- });
89
- return engineProcess;
90
- }
91
- /**
92
- * Send a command to the crypto engine and get the response.
93
- */
94
- function sendCommand(request) {
95
- return new Promise((resolve, reject) => {
96
- const engine = getEngine();
97
- const onData = (data) => {
98
- try {
99
- const response = JSON.parse(data.toString().trim());
100
- engine.stdout.removeListener("data", onData);
101
- engine.stderr.removeListener("data", onError);
102
- if (response.status === "error") {
103
- reject(new Error(response.message));
104
- }
105
- else {
106
- resolve(response);
107
- }
108
- }
109
- catch (e) {
110
- // partial data, wait for more
111
- }
112
- };
113
- const onError = (data) => {
114
- engine.stdout.removeListener("data", onData);
115
- engine.stderr.removeListener("data", onError);
116
- reject(new Error(`Crypto engine error: ${data.toString()}`));
117
- };
118
- engine.stdout.on("data", onData);
119
- engine.stderr.on("data", onError);
120
- engine.stdin.write(JSON.stringify(request) + "\n");
121
- });
11
+ const libsodium_wrappers_1 = __importDefault(require("libsodium-wrappers"));
12
+ const buffer_1 = require("buffer");
13
+ async function getSodium() {
14
+ await libsodium_wrappers_1.default.ready;
15
+ return libsodium_wrappers_1.default;
122
16
  }
123
- /**
124
- * Generate a new X25519 key pair.
125
- */
126
17
  async function generateKeys() {
127
- const result = await sendCommand({ action: "generate_keys" });
18
+ const sodium = await getSodium();
19
+ const keypair = sodium.crypto_box_keypair();
128
20
  return {
129
- private_key: result.private_key,
130
- public_key: result.public_key,
21
+ private_key: buffer_1.Buffer.from(keypair.privateKey).toString("base64"),
22
+ public_key: buffer_1.Buffer.from(keypair.publicKey).toString("base64"),
131
23
  };
132
24
  }
133
- /**
134
- * Derive the public key from an existing private key.
135
- */
136
25
  async function derivePublicKey(privateKey) {
137
- const result = await sendCommand({
138
- action: "derive_public_key",
139
- data: { private_key: privateKey },
140
- });
141
- return result.public_key;
26
+ const sodium = await getSodium();
27
+ const sk = buffer_1.Buffer.from(privateKey, "base64");
28
+ const pk = sodium.crypto_scalarmult_base(sk);
29
+ return buffer_1.Buffer.from(pk).toString("base64");
142
30
  }
143
- /**
144
- * Encrypt a plaintext value using the recipient's public key (SealedBox).
145
- */
146
31
  async function encrypt(plaintext, publicKey) {
147
- const result = await sendCommand({
148
- action: "encrypt",
149
- data: { plaintext, public_key: publicKey },
150
- });
151
- return result.ciphertext;
32
+ const sodium = await getSodium();
33
+ const pk = buffer_1.Buffer.from(publicKey, "base64");
34
+ const encrypted = sodium.crypto_box_seal(sodium.from_string(plaintext), pk);
35
+ return buffer_1.Buffer.from(encrypted).toString("base64");
152
36
  }
153
- /**
154
- * Decrypt a ciphertext using the recipient's private key (SealedBox).
155
- */
156
37
  async function decrypt(ciphertext, privateKey) {
157
- const result = await sendCommand({
158
- action: "decrypt",
159
- data: { ciphertext, private_key: privateKey },
160
- });
161
- return result.plaintext;
38
+ const sodium = await getSodium();
39
+ const ct = buffer_1.Buffer.from(ciphertext, "base64");
40
+ const sk = buffer_1.Buffer.from(privateKey, "base64");
41
+ // Need to compute public key corresponding to private key for unsealing
42
+ const pk = sodium.crypto_scalarmult_base(sk);
43
+ const decrypted = sodium.crypto_box_seal_open(ct, pk, sk);
44
+ return sodium.to_string(decrypted);
162
45
  }
163
- /**
164
- * Shut down the crypto engine process.
165
- */
166
46
  function shutdown() {
167
- if (engineProcess && !engineProcess.killed) {
168
- engineProcess.stdin.end();
169
- engineProcess.kill();
170
- engineProcess = null;
171
- }
47
+ // Graceful stub for backward compatibility
172
48
  }
173
49
  //# sourceMappingURL=crypto.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/lib/crypto.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/lib/crypto.ts"],"names":[],"mappings":";;;;;AAQA,oCAUC;AAED,0CAKC;AAED,0BAQC;AAED,0BAaC;AAED,4BAEC;AAtDD,4EAAyC;AACzC,mCAAgC;AAEhC,KAAK,UAAU,SAAS;IACtB,MAAM,4BAAO,CAAC,KAAK,CAAC;IACpB,OAAO,4BAAO,CAAC;AACjB,CAAC;AAEM,KAAK,UAAU,YAAY;IAIhC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,CAAC,kBAAkB,EAAE,CAAC;IAC5C,OAAO;QACL,WAAW,EAAE,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC/D,UAAU,EAAE,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAC9D,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,UAAkB;IACtD,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IACjC,MAAM,EAAE,GAAG,eAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,MAAM,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAC7C,OAAO,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAEM,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,SAAiB;IAEjB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IACjC,MAAM,EAAE,GAAG,eAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC;IAC5E,OAAO,eAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACnD,CAAC;AAEM,KAAK,UAAU,OAAO,CAC3B,UAAkB,EAClB,UAAkB;IAElB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IACjC,MAAM,EAAE,GAAG,eAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,eAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAE7C,wEAAwE;IACxE,MAAM,EAAE,GAAG,MAAM,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAE7C,MAAM,SAAS,GAAG,MAAM,CAAC,oBAAoB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;AACrC,CAAC;AAED,SAAgB,QAAQ;IACtB,2CAA2C;AAC7C,CAAC"}
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@etree/cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "private": false,
5
+ "files": [
6
+ "dist"
7
+ ],
5
8
  "bin": {
6
- "et": "./dist/index.js",
7
- "envtree": "./dist/index.js"
9
+ "et": "dist/index.js",
10
+ "envtree": "dist/index.js"
8
11
  },
9
12
  "scripts": {
10
13
  "dev": "tsc --watch",
@@ -17,12 +20,14 @@
17
20
  "commander": "^13.1.0",
18
21
  "conf": "^10.2.0",
19
22
  "inquirer": "^8.2.6",
23
+ "libsodium-wrappers": "^0.8.2",
20
24
  "ora": "^5.4.1"
21
25
  },
22
26
  "devDependencies": {
23
27
  "@envtree/eslint-config": "workspace:*",
24
28
  "@envtree/typescript-config": "workspace:*",
25
29
  "@types/inquirer": "^8.2.10",
30
+ "@types/libsodium-wrappers": "^0.8.2",
26
31
  "@types/node": "^22.15.2",
27
32
  "tsx": "^4.19.4",
28
33
  "typescript": "5.9.2"
package/eslint.config.mjs DELETED
@@ -1,4 +0,0 @@
1
- import { config } from "@envtree/eslint-config/base";
2
-
3
- /** @type {import("eslint").Linter.Config[]} */
4
- export default config;
@@ -1,258 +0,0 @@
1
- import { Command } from "commander";
2
- import inquirer from "inquirer";
3
- import chalk from "chalk";
4
- import ora from "ora";
5
- import { apiRequest } from "../lib/api";
6
- import { saveSession, clearSession, getSession } from "../lib/config";
7
- import { generateKeys, derivePublicKey, shutdown } from "../lib/crypto";
8
- import { savePrivateKey, hasPrivateKey } from "../lib/key-store";
9
-
10
- export function registerAuthCommands(program: Command): void {
11
- // ── signup ──
12
- program
13
- .command("signup")
14
- .description("Create a new EnvTree account")
15
- .action(async () => {
16
- const answers = await inquirer.prompt([
17
- { type: "input", name: "email", message: "Email:" },
18
- { type: "input", name: "username", message: "Username:" },
19
- {
20
- type: "password",
21
- name: "password",
22
- message: "Password:",
23
- mask: "*",
24
- },
25
- {
26
- type: "password",
27
- name: "confirmPassword",
28
- message: "Confirm Password:",
29
- mask: "*",
30
- },
31
- ]);
32
-
33
- if (answers.password !== answers.confirmPassword) {
34
- console.log(chalk.red("Passwords do not match"));
35
- return;
36
- }
37
-
38
- const spinner = ora("Creating account...").start();
39
- try {
40
- const result = await apiRequest(
41
- "POST",
42
- "/auth/signup",
43
- {
44
- email: answers.email,
45
- password: answers.password,
46
- username: answers.username,
47
- },
48
- false,
49
- );
50
-
51
- if (!result.session) {
52
- spinner.succeed(
53
- "Account created! Please verify your email, then run: et login",
54
- );
55
- return;
56
- }
57
-
58
- saveSession({
59
- access_token: result.session.access_token,
60
- refresh_token: result.session.refresh_token,
61
- user_id: result.user.id,
62
- email: answers.email,
63
- username: answers.username,
64
- });
65
-
66
- spinner.text = "Generating encryption keys...";
67
- const keys = await generateKeys();
68
- savePrivateKey(keys.private_key);
69
-
70
- await apiRequest("PUT", "/profile/public-key", {
71
- public_key: keys.public_key,
72
- });
73
-
74
- spinner.succeed(chalk.green("Account created & keys generated!"));
75
- console.log(
76
- chalk.dim(` User: ${answers.username} (${answers.email})`),
77
- );
78
- console.log(
79
- chalk.dim(" Private key saved to ~/.envtree/private_key"),
80
- );
81
- } catch (err: any) {
82
- spinner.fail(chalk.red(`Signup failed: ${err.message}`));
83
- } finally {
84
- shutdown();
85
- }
86
- });
87
-
88
- // ── login ──
89
- program
90
- .command("login")
91
- .description("Log in to your EnvTree account")
92
- .option("-e, --email <email>", "Email (non-interactive)")
93
- .option("-p, --password <password>", "Password (non-interactive)")
94
- .action(async (opts: { email?: string; password?: string }) => {
95
- let email = opts.email;
96
- let password = opts.password;
97
-
98
- // Interactive mode if flags not provided
99
- if (!email || !password) {
100
- const answers = await inquirer.prompt([
101
- ...(!email
102
- ? [{ type: "input", name: "email", message: "Email:" }]
103
- : []),
104
- ...(!password
105
- ? [
106
- {
107
- type: "password",
108
- name: "password",
109
- message: "Password:",
110
- mask: "*",
111
- },
112
- ]
113
- : []),
114
- ]);
115
- email = email || answers.email;
116
- password = password || answers.password;
117
- }
118
-
119
- const spinner = ora("Logging in...").start();
120
- try {
121
- const result = await apiRequest(
122
- "POST",
123
- "/auth/login",
124
- { email, password },
125
- false,
126
- );
127
-
128
- saveSession({
129
- access_token: result.session.access_token,
130
- refresh_token: result.session.refresh_token,
131
- user_id: result.user.id,
132
- email: result.user.email,
133
- username: email!,
134
- });
135
-
136
- // Fetch profile to get real username
137
- const profileResult = await apiRequest(
138
- "GET",
139
- "/profile/me",
140
- undefined,
141
- true,
142
- );
143
- if (profileResult.profile?.username) {
144
- saveSession({
145
- access_token: result.session.access_token,
146
- refresh_token: result.session.refresh_token,
147
- user_id: result.user.id,
148
- email: result.user.email,
149
- username: profileResult.profile.username,
150
- });
151
- }
152
-
153
- // Always sync public key with server
154
- if (hasPrivateKey()) {
155
- // Derive public key from existing private key and upload it
156
- spinner.text = "Syncing encryption keys...";
157
- const privateKey = require("../lib/key-store").getPrivateKey();
158
- const publicKey = await derivePublicKey(privateKey);
159
- await apiRequest("PUT", "/profile/public-key", {
160
- public_key: publicKey,
161
- });
162
- spinner.succeed(chalk.green("Logged in!"));
163
- } else {
164
- // Generate a new keypair
165
- spinner.text = "Generating encryption keys...";
166
- const keys = await generateKeys();
167
- savePrivateKey(keys.private_key);
168
-
169
- await apiRequest("PUT", "/profile/public-key", {
170
- public_key: keys.public_key,
171
- });
172
-
173
- spinner.succeed(chalk.green("Logged in & keys generated!"));
174
- console.log(
175
- chalk.dim(" Private key saved to ~/.envtree/private_key"),
176
- );
177
- }
178
-
179
- console.log(chalk.dim(` User: ${result.user.email}`));
180
- } catch (err: any) {
181
- spinner.fail(chalk.red(`Login failed: ${err.message}`));
182
- } finally {
183
- shutdown();
184
- }
185
- });
186
-
187
- // ── logout ──
188
- program
189
- .command("logout")
190
- .description("Log out of your EnvTree account")
191
- .action(() => {
192
- clearSession();
193
- console.log(chalk.green("Logged out"));
194
- });
195
-
196
- // ── whoami ──
197
- program
198
- .command("whoami")
199
- .description("Show the current logged-in user")
200
- .action(async () => {
201
- const session = getSession();
202
- if (!session) {
203
- console.log(chalk.yellow("Not logged in. Run: et login"));
204
- return;
205
- }
206
-
207
- try {
208
- const result = await apiRequest("GET", "/profile/me");
209
- console.log(chalk.bold("EnvTree User"));
210
- console.log(` Username: ${result.profile.username}`);
211
- console.log(` Email: ${result.profile.email}`);
212
- console.log(
213
- ` Public Key: ${result.profile.public_key ? result.profile.public_key.substring(0, 20) + "..." : chalk.yellow("not set")}`,
214
- );
215
- } catch {
216
- console.log(chalk.dim(` Username: ${session.username}`));
217
- console.log(chalk.dim(` Email: ${session.email}`));
218
- console.log(chalk.yellow(" (offline — showing cached info)"));
219
- }
220
- });
221
-
222
- // ── delete-account ──
223
- program
224
- .command("delete-account")
225
- .description("Permanently delete your EnvTree account")
226
- .action(async () => {
227
- const session = getSession();
228
- if (!session) {
229
- console.log(chalk.yellow("Not logged in. Run: et login"));
230
- return;
231
- }
232
-
233
- const { confirm } = await inquirer.prompt([
234
- {
235
- type: "confirm",
236
- name: "confirm",
237
- message: chalk.red(
238
- "⚠ This will permanently delete your account and all your wallets. Are you sure?",
239
- ),
240
- default: false,
241
- },
242
- ]);
243
-
244
- if (!confirm) {
245
- console.log(chalk.dim("Cancelled."));
246
- return;
247
- }
248
-
249
- const spinner = ora("Deleting account...").start();
250
- try {
251
- await apiRequest("DELETE", "/auth/account");
252
- clearSession();
253
- spinner.succeed(chalk.green("Account deleted. Goodbye!"));
254
- } catch (err: any) {
255
- spinner.fail(chalk.red(`Failed: ${err.message}`));
256
- }
257
- });
258
- }
@@ -1,47 +0,0 @@
1
- import { Command } from "commander";
2
- import chalk from "chalk";
3
- import { getConfig, saveConfig } from "../lib/config";
4
-
5
- export function registerConfigCommands(program: Command): void {
6
- const config = program
7
- .command("config")
8
- .description("Manage CLI configuration");
9
-
10
- // ── config show ──
11
- config
12
- .command("show")
13
- .description("Display current configuration")
14
- .action(() => {
15
- const cfg = getConfig();
16
- console.log(chalk.bold("\nEnvTree Config"));
17
- console.log(` Server: ${chalk.cyan(cfg.server_url)}`);
18
- console.log(
19
- ` Session: ${cfg.session ? chalk.green("active") + chalk.dim(` (${cfg.session.email})`) : chalk.yellow("none")}`,
20
- );
21
- console.log(
22
- chalk.dim(` Config: ~/.envtree/config.json`),
23
- );
24
- console.log(
25
- chalk.dim(` Keys: ~/.envtree/private_key`),
26
- );
27
- console.log("");
28
- });
29
-
30
- // ── config set ──
31
- config
32
- .command("set <key> <value>")
33
- .description("Set a configuration value (e.g. server URL)")
34
- .action((key: string, value: string) => {
35
- const cfg = getConfig();
36
-
37
- if (key === "server" || key === "server_url") {
38
- cfg.server_url = value;
39
- saveConfig(cfg);
40
- console.log(chalk.green(`Server URL set to: ${value}`));
41
- } else {
42
- console.log(
43
- chalk.red(`Unknown config key: "${key}". Available: server`),
44
- );
45
- }
46
- });
47
- }