@etree/cli 2.0.1 → 2.0.2

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/src/lib/crypto.ts DELETED
@@ -1,173 +0,0 @@
1
- import { spawn, ChildProcessWithoutNullStreams } from "child_process";
2
- import * as path from "path";
3
-
4
- const CRYPTO_ENGINE_PATH = path.resolve(
5
- __dirname,
6
- "../../../../packages/crypto/engine.py",
7
- );
8
-
9
- let engineProcess: ChildProcessWithoutNullStreams | null = null;
10
-
11
- interface CryptoRequest {
12
- action: string;
13
- data?: Record<string, any>;
14
- }
15
-
16
- interface CryptoResponse {
17
- status: string;
18
- [key: string]: any;
19
- }
20
-
21
- /**
22
- * Get or spawn the crypto engine Python process.
23
- */
24
- function getEngine(): ChildProcessWithoutNullStreams {
25
- if (engineProcess && !engineProcess.killed) {
26
- return engineProcess;
27
- }
28
-
29
- // Try venv python first, fall back to system python
30
- const venvDir = path.resolve(__dirname, "../../../../packages/crypto/.venv");
31
- const isWindows = process.platform === "win32";
32
-
33
- // Potential Venv Paths
34
- const potentialVenvPaths = isWindows
35
- ? [
36
- path.join(venvDir, "Scripts", "python.exe"),
37
- path.join(venvDir, "bin", "python.exe"),
38
- ]
39
- : [path.join(venvDir, "bin", "python")];
40
-
41
- let pythonCmd = isWindows ? "python" : "python3";
42
-
43
- for (const venvPath of potentialVenvPaths) {
44
- if (require("fs").existsSync(venvPath)) {
45
- pythonCmd = venvPath;
46
- break;
47
- }
48
- }
49
-
50
- // If we haven't found a venv python, verify the system command exists
51
- if (pythonCmd === "python" || pythonCmd === "python3") {
52
- try {
53
- // Use system python if it exists and works
54
- require("child_process").execSync(`${pythonCmd} --version`, {
55
- stdio: "ignore",
56
- });
57
- } catch {
58
- // If the preferred one fails, try the fallback
59
- pythonCmd = pythonCmd === "python3" ? "python" : "python3";
60
- }
61
- }
62
-
63
- engineProcess = spawn(pythonCmd, [CRYPTO_ENGINE_PATH], {
64
- stdio: ["pipe", "pipe", "pipe"],
65
- cwd: path.resolve(__dirname, "../../../../packages/crypto"),
66
- });
67
-
68
- engineProcess.on("exit", () => {
69
- engineProcess = null;
70
- });
71
-
72
- return engineProcess;
73
- }
74
-
75
- /**
76
- * Send a command to the crypto engine and get the response.
77
- */
78
- function sendCommand(request: CryptoRequest): Promise<CryptoResponse> {
79
- return new Promise((resolve, reject) => {
80
- const engine = getEngine();
81
-
82
- const onData = (data: Buffer) => {
83
- try {
84
- const response = JSON.parse(data.toString().trim());
85
- engine.stdout.removeListener("data", onData);
86
- engine.stderr.removeListener("data", onError);
87
-
88
- if (response.status === "error") {
89
- reject(new Error(response.message));
90
- } else {
91
- resolve(response);
92
- }
93
- } catch (e) {
94
- // partial data, wait for more
95
- }
96
- };
97
-
98
- const onError = (data: Buffer) => {
99
- engine.stdout.removeListener("data", onData);
100
- engine.stderr.removeListener("data", onError);
101
- reject(new Error(`Crypto engine error: ${data.toString()}`));
102
- };
103
-
104
- engine.stdout.on("data", onData);
105
- engine.stderr.on("data", onError);
106
-
107
- engine.stdin.write(JSON.stringify(request) + "\n");
108
- });
109
- }
110
-
111
- /**
112
- * Generate a new X25519 key pair.
113
- */
114
- export async function generateKeys(): Promise<{
115
- private_key: string;
116
- public_key: string;
117
- }> {
118
- const result = await sendCommand({ action: "generate_keys" });
119
- return {
120
- private_key: result.private_key,
121
- public_key: result.public_key,
122
- };
123
- }
124
-
125
- /**
126
- * Derive the public key from an existing private key.
127
- */
128
- export async function derivePublicKey(privateKey: string): Promise<string> {
129
- const result = await sendCommand({
130
- action: "derive_public_key",
131
- data: { private_key: privateKey },
132
- });
133
- return result.public_key;
134
- }
135
-
136
- /**
137
- * Encrypt a plaintext value using the recipient's public key (SealedBox).
138
- */
139
- export async function encrypt(
140
- plaintext: string,
141
- publicKey: string,
142
- ): Promise<string> {
143
- const result = await sendCommand({
144
- action: "encrypt",
145
- data: { plaintext, public_key: publicKey },
146
- });
147
- return result.ciphertext;
148
- }
149
-
150
- /**
151
- * Decrypt a ciphertext using the recipient's private key (SealedBox).
152
- */
153
- export async function decrypt(
154
- ciphertext: string,
155
- privateKey: string,
156
- ): Promise<string> {
157
- const result = await sendCommand({
158
- action: "decrypt",
159
- data: { ciphertext, private_key: privateKey },
160
- });
161
- return result.plaintext;
162
- }
163
-
164
- /**
165
- * Shut down the crypto engine process.
166
- */
167
- export function shutdown(): void {
168
- if (engineProcess && !engineProcess.killed) {
169
- engineProcess.stdin.end();
170
- engineProcess.kill();
171
- engineProcess = null;
172
- }
173
- }
@@ -1,60 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
-
4
- /**
5
- * Updates a key=value pair in a .env file.
6
- * Creates the file if it doesn't exist.
7
- * If the key exists, it updates its value.
8
- * If the key doesn't exist, it appends to the end.
9
- */
10
- export function updateEnvFile(key: string, value: string): void {
11
- const envPath = path.resolve(process.cwd(), ".env");
12
- let content = "";
13
-
14
- if (fs.existsSync(envPath)) {
15
- content = fs.readFileSync(envPath, "utf-8");
16
- }
17
-
18
- const lines = content.split("\n");
19
- const keyPattern = new RegExp(`^${key}=.*`);
20
- let found = false;
21
-
22
- const newLines = lines.map((line) => {
23
- if (keyPattern.test(line.trim())) {
24
- found = true;
25
- return `${key}=${value}`;
26
- }
27
- return line;
28
- });
29
-
30
- if (!found) {
31
- if (newLines.length > 0 && newLines[newLines.length - 1].trim() !== "") {
32
- newLines.push("");
33
- }
34
- newLines.push(`${key}=${value}`);
35
- }
36
-
37
- fs.writeFileSync(envPath, newLines.join("\n"), "utf-8");
38
- }
39
-
40
- /**
41
- * Ensures that a .gitignore file exists and contains the specified entry.
42
- */
43
- export function ensureGitIgnore(entry: string = ".env"): void {
44
- const gitIgnorePath = path.resolve(process.cwd(), ".gitignore");
45
- let content = "";
46
-
47
- if (fs.existsSync(gitIgnorePath)) {
48
- content = fs.readFileSync(gitIgnorePath, "utf-8");
49
- }
50
-
51
- const lines = content.split("\n").map((l) => l.trim());
52
-
53
- if (!lines.includes(entry)) {
54
- if (content.length > 0 && !content.endsWith("\n")) {
55
- content += "\n";
56
- }
57
- content += `${entry}\n`;
58
- fs.writeFileSync(gitIgnorePath, content, "utf-8");
59
- }
60
- }
@@ -1,51 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import * as os from "os";
4
-
5
- const KEY_DIR = path.join(os.homedir(), ".envtree");
6
- const KEY_FILE = path.join(KEY_DIR, "private_key");
7
-
8
- function ensureKeyDir(): void {
9
- if (!fs.existsSync(KEY_DIR)) {
10
- fs.mkdirSync(KEY_DIR, { recursive: true, mode: 0o700 });
11
- }
12
- }
13
-
14
- /**
15
- * Save the private key to ~/.envtree/private_key
16
- */
17
- export function savePrivateKey(privateKey: string): void {
18
- ensureKeyDir();
19
- fs.writeFileSync(KEY_FILE, privateKey, { mode: 0o600 });
20
- }
21
-
22
- /**
23
- * Read the private key from ~/.envtree/private_key
24
- */
25
- export function getPrivateKey(): string | null {
26
- if (!fs.existsSync(KEY_FILE)) {
27
- return null;
28
- }
29
- return fs.readFileSync(KEY_FILE, "utf-8").trim();
30
- }
31
-
32
- /**
33
- * Get private key or exit with error.
34
- */
35
- export function requirePrivateKey(): string {
36
- const key = getPrivateKey();
37
- if (!key) {
38
- console.error(
39
- "Private key not found. Run: envtree signup (or envtree login)",
40
- );
41
- process.exit(1);
42
- }
43
- return key;
44
- }
45
-
46
- /**
47
- * Check if a private key exists locally.
48
- */
49
- export function hasPrivateKey(): boolean {
50
- return fs.existsSync(KEY_FILE);
51
- }
package/src/test-e2e.ts DELETED
@@ -1,106 +0,0 @@
1
- import { apiRequest } from "./lib/api";
2
- import { saveSession } from "./lib/config";
3
- import { generateKeys, encrypt, decrypt, shutdown } from "./lib/crypto";
4
- import { savePrivateKey } from "./lib/key-store";
5
- import * as fs from "fs";
6
- import * as path from "os";
7
-
8
- async function testE2E() {
9
- const email = `test-${Date.now()}@example.com`;
10
- const username = `user-${Date.now()}`;
11
- const password = "password123";
12
-
13
- console.log("--- Phase 1: Signup ---");
14
- try {
15
- const signupResult = await apiRequest(
16
- "POST",
17
- "/auth/signup",
18
- {
19
- email,
20
- password,
21
- username,
22
- },
23
- false,
24
- );
25
-
26
- console.log("Signup response:", JSON.stringify(signupResult, null, 2));
27
-
28
- if (signupResult.session) {
29
- saveSession({
30
- access_token: signupResult.session.access_token,
31
- refresh_token: signupResult.session.refresh_token,
32
- user_id: signupResult.user.id,
33
- email,
34
- username,
35
- });
36
- console.log(" Signup successful");
37
- } else {
38
- console.log(
39
- " User created but NO session returned (possibly email verification required)",
40
- );
41
- }
42
-
43
- if (!signupResult.session) {
44
- console.warn(
45
- "⚠️ Ending test early: No session available to continue authenticated requests.",
46
- );
47
- return;
48
- }
49
-
50
- console.log("--- Phase 2: Key Generation ---");
51
- const { private_key, public_key } = await generateKeys();
52
- savePrivateKey(private_key);
53
- await apiRequest("PUT", "/profile/public-key", { public_key });
54
- console.log(" Keypair generated and public key uploaded");
55
-
56
- console.log("--- Phase 3: Wallet Creation ---");
57
- const walletName = `test-wallet-${Date.now()}`;
58
- const walletResult = await apiRequest("POST", "/wallets", {
59
- name: walletName,
60
- });
61
- const walletId = walletResult.wallet.id;
62
- console.log(` Wallet "${walletName}" created with ID: ${walletId}`);
63
-
64
- console.log("--- Phase 4: Secret Encryption & Storage ---");
65
- const secretKey = "MY_API_KEY";
66
- const secretValue = "super-secret-value-123";
67
- const encryptedValue = await encrypt(secretValue, public_key);
68
-
69
- await apiRequest("POST", `/wallets/${walletId}/secrets`, {
70
- secrets: [
71
- {
72
- key_name: secretKey,
73
- encrypted_value: encryptedValue,
74
- encrypted_for: public_key,
75
- },
76
- ],
77
- });
78
- console.log(" Secret encrypted and uploaded");
79
-
80
- console.log("--- Phase 5: Secret Retrieval & Decryption ---");
81
- const getResult = await apiRequest("GET", `/wallets/${walletId}/secrets`);
82
- const foundSecret = getResult.secrets.find(
83
- (s: any) => s.key_name === secretKey,
84
- );
85
-
86
- if (!foundSecret) throw new Error("Secret not found in response");
87
-
88
- const decryptedValue = await decrypt(
89
- foundSecret.encrypted_value,
90
- private_key,
91
- );
92
- console.log(` Secret retrieved and decrypted: ${decryptedValue}`);
93
-
94
- if (decryptedValue === secretValue) {
95
- console.log(" E2E TRANSACTION SUCCESSFUL");
96
- } else {
97
- throw new Error("Decrypted value mismatch");
98
- }
99
- } catch (error) {
100
- console.error(" E2E TEST FAILED:", error);
101
- } finally {
102
- shutdown();
103
- }
104
- }
105
-
106
- testE2E();
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "commonjs",
5
- "lib": ["ES2020"],
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "resolveJsonModule": true,
13
- "declaration": true,
14
- "sourceMap": true
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["node_modules", "dist"]
18
- }