enver-cli 1.0.0

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 ADDED
@@ -0,0 +1,114 @@
1
+ # 🔐 Enver
2
+
3
+ **Enver** is a secure, cloud-native CLI tool for managing environment variables. It seamlessly integrates with **Google Drive** to provide encrypted backups, versioning, and smart restoration for your local `.env` files.
4
+
5
+ ---
6
+
7
+ ## ✨ Key Features
8
+
9
+ - **🛡️ AES-256-CBC Encryption**: All environment files are encrypted locally with a developer-defined password BEFORE they touch the cloud.
10
+ - **☁️ Google Drive Integration**: Uses your personal or workspace Google Drive as a reliable, zero-cost backup solution.
11
+ - **🚀 Advanced Restoration**:
12
+ - **Interactive Diff**: Preview line-by-line changes before restoring.
13
+ - **Smart Selection**: Choose between specific versions or instantly grab the latest backup.
14
+ - **📁 Automated Organization**:
15
+ - Automatically organizes backups by `project-name` and `version` (from `package.json`).
16
+ - **Auto-Prunning**: Keeps only the last 5 backups per version to optimize storage.
17
+ - **⚙️ Configurable Security**: Enable/disable encryption or set a global default password for seamless automation.
18
+
19
+ ---
20
+
21
+ ## 🛠️ Getting Started
22
+
23
+ ### 1. Installation
24
+
25
+ Clone the repository and install dependencies:
26
+
27
+ ```bash
28
+ git clone <your-repo-url>
29
+ cd Enver
30
+ npm install
31
+ npm run build
32
+ ```
33
+
34
+ ### 2. Authentication
35
+
36
+ Link your Google account to authorize Drive access:
37
+
38
+ ```bash
39
+ enver login
40
+ ```
41
+
42
+ _This will open a browser window for Secure OAuth2 authentication._
43
+
44
+ ### 3. (Optional) Configuration
45
+
46
+ Set a global encryption password so you don't get prompted every time:
47
+
48
+ ```bash
49
+ enver config password
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 🚀 Usage
55
+
56
+ ### Backup your Environment
57
+
58
+ Create an encrypted backup of your current `.env` file:
59
+
60
+ ```bash
61
+ enver push
62
+ ```
63
+
64
+ _The tool automatically detects your project name and version from `package.json`._
65
+
66
+ ### Restore your Environment
67
+
68
+ Pull a backup back from Google Drive:
69
+
70
+ ```bash
71
+ enver pull
72
+ ```
73
+
74
+ _You can choose a specific backup or use `enver pull --latest` to skip selection._
75
+
76
+ ### Toggle Encryption
77
+
78
+ If you are working in a safe local environment, you can disable encryption:
79
+
80
+ ```bash
81
+ enver config encryption true/false
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 📁 Storage Architecture
87
+
88
+ Enver creates a structured hierarchy in your Google Drive:
89
+
90
+ ```text
91
+ Google Drive
92
+ └── Enver (Root Namespace)
93
+ └── [project-name]-[v1.0.0]
94
+ ├── [project]_.env_2026-02-10_02-46-20.enc
95
+ └── [project]_.env_2026-02-11_10-00-00.enc
96
+ ```
97
+
98
+ ---
99
+
100
+ ## 🔒 Security Principles
101
+
102
+ 1. **Zero-Knowledge**: Your encryption password is never sent to Google and never stored on our servers.
103
+ 2. **Local Encryption**: The CLI encrypts the payload in-memory before transmission.
104
+ 3. **Secure Config**: Local configurations (including tokens) are stored in your home directory (`~/.enver/`) with restricted permissions.
105
+
106
+ ---
107
+
108
+ ## 🤝 Contributing
109
+
110
+ Contributions are welcome! Please feel free to submit a Pull Request.
111
+
112
+ ## 📄 License
113
+
114
+ MIT © 2026
package/bin/index.ts ADDED
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import dotenv from 'dotenv';
5
+ import inquirer from 'inquirer';
6
+ import chalk from 'chalk';
7
+ import { getAuthenticatedClient, logout } from '../src/auth';
8
+ import { uploadEnv, downloadEnv } from '../src/drive';
9
+ import { setPassword, setEncryption, resetConfig } from '../src/config';
10
+
11
+ dotenv.config();
12
+
13
+ const program = new Command();
14
+
15
+ program
16
+ .name('enver')
17
+ .description('Backup and restore your local .env files with encryption and versioning')
18
+ .version('2.2.0', '-v, -V, --version');
19
+
20
+ async function runSetup() {
21
+ console.log(chalk.blue('\n--- Initial Setup ---'));
22
+
23
+ const { encryption } = await inquirer.prompt([{
24
+ type: 'confirm',
25
+ name: 'encryption',
26
+ message: 'Enable encryption globally by default?',
27
+ default: false,
28
+ }]);
29
+
30
+ await setEncryption(encryption);
31
+
32
+ const { setPass } = await inquirer.prompt([{
33
+ type: 'confirm',
34
+ name: 'setPass',
35
+ message: 'Set a default encryption password now? (saves you from manual prompts)',
36
+ default: false,
37
+ }]);
38
+
39
+ if (setPass) {
40
+ const { password } = await inquirer.prompt([{
41
+ type: 'password',
42
+ name: 'password',
43
+ message: 'Enter your default password:',
44
+ mask: '*',
45
+ }]);
46
+ await setPassword(password);
47
+ }
48
+
49
+ console.log(chalk.green('\nSetup complete! You can change these anytime using "enver config".\n'));
50
+ }
51
+
52
+ program
53
+ .command('login')
54
+ .description('Authenticate with your Google account and run setup')
55
+ .action(async () => {
56
+ try {
57
+ await getAuthenticatedClient();
58
+ console.log(chalk.green('Successfully authenticated!'));
59
+ await runSetup();
60
+ } catch (error: any) {
61
+ console.error(chalk.red('Authentication failed:'), error.message);
62
+ }
63
+ });
64
+
65
+ program
66
+ .command('logout')
67
+ .description('Sign out and remove authentication tokens')
68
+ .action(async () => {
69
+ await logout();
70
+ console.log(chalk.yellow('Successfully logged out. Credentials removed locally.'));
71
+ });
72
+
73
+ program
74
+ .command('push')
75
+ .description('Upload local .env (encrypted) to Google Drive')
76
+ .option('-f, --file <filename>', 'Local filename to backup (default: .env)')
77
+ .action(async (options) => {
78
+ await uploadEnv({ file: options.file });
79
+ });
80
+
81
+ program
82
+ .command('pull')
83
+ .description('Restore .env from Google Drive with diff preview')
84
+ .option('-f, --file <filename>', 'Local filename to restore to (default: .env)')
85
+ .option('-n, --name <project>', 'Specific project folder name')
86
+ .option('-l, --latest', 'Automatically pick the latest backup')
87
+ .action(async (options) => {
88
+ await downloadEnv({
89
+ file: options.file,
90
+ name: options.name,
91
+ latest: options.latest
92
+ });
93
+ });
94
+
95
+ const config = program.command('config').description('Manage CLI configuration');
96
+
97
+ config
98
+ .command('password')
99
+ .description('Set, update, or remove the default encryption password')
100
+ .action(async () => {
101
+ const { action } = await inquirer.prompt([{
102
+ type: 'list',
103
+ name: 'action',
104
+ message: 'What would you like to do?',
105
+ choices: ['Set/Update Password', 'Remove Password', 'Cancel'],
106
+ }]);
107
+
108
+ if (action === 'Set/Update Password') {
109
+ const { password } = await inquirer.prompt([{
110
+ type: 'password',
111
+ name: 'password',
112
+ message: 'Enter new default password:',
113
+ mask: '*',
114
+ }]);
115
+ await setPassword(password);
116
+ console.log(chalk.green('Default password updated!'));
117
+ } else if (action === 'Remove Password') {
118
+ await setPassword(undefined);
119
+ console.log(chalk.yellow('Default password removed. You will be prompted manually for future operations.'));
120
+ }
121
+ });
122
+
123
+ config
124
+ .command('encryption <state>')
125
+ .description('Enable or disable encryption globally (on|off)')
126
+ .action(async (state) => {
127
+ if (state === 'on') {
128
+ await setEncryption(true);
129
+ console.log(chalk.green('Encryption enabled! Local files will be encrypted before upload.'));
130
+ } else if (state === 'off') {
131
+ await setEncryption(false);
132
+ console.log(chalk.yellow('Encryption disabled! Backups will be stored as plain text.'));
133
+ } else {
134
+ console.error(chalk.red('Invalid state. Use "on" or "off".'));
135
+ }
136
+ });
137
+
138
+ config
139
+ .command('reset')
140
+ .description('Reset all global configurations to factory defaults')
141
+ .action(async () => {
142
+ const { confirm } = await inquirer.prompt([{
143
+ type: 'confirm',
144
+ name: 'confirm',
145
+ message: chalk.red('Are you sure you want to reset all configurations? This cannot be undone.'),
146
+ default: false,
147
+ }]);
148
+
149
+ if (confirm) {
150
+ await resetConfig();
151
+ console.log(chalk.green('Configuration reset successfully! Settings are back to factory defaults.'));
152
+ }
153
+ });
154
+
155
+ program.parse(process.argv);
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const dotenv_1 = __importDefault(require("dotenv"));
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const auth_1 = require("../src/auth");
12
+ const drive_1 = require("../src/drive");
13
+ const config_1 = require("../src/config");
14
+ dotenv_1.default.config();
15
+ const program = new commander_1.Command();
16
+ program
17
+ .name('enver')
18
+ .description('Backup and restore your local .env files with encryption and versioning')
19
+ .version('2.2.0', '-v, -V, --version');
20
+ async function runSetup() {
21
+ console.log(chalk_1.default.blue('\n--- Initial Setup ---'));
22
+ const { encryption } = await inquirer_1.default.prompt([{
23
+ type: 'confirm',
24
+ name: 'encryption',
25
+ message: 'Enable encryption globally by default?',
26
+ default: false,
27
+ }]);
28
+ await (0, config_1.setEncryption)(encryption);
29
+ const { setPass } = await inquirer_1.default.prompt([{
30
+ type: 'confirm',
31
+ name: 'setPass',
32
+ message: 'Set a default encryption password now? (saves you from manual prompts)',
33
+ default: false,
34
+ }]);
35
+ if (setPass) {
36
+ const { password } = await inquirer_1.default.prompt([{
37
+ type: 'password',
38
+ name: 'password',
39
+ message: 'Enter your default password:',
40
+ mask: '*',
41
+ }]);
42
+ await (0, config_1.setPassword)(password);
43
+ }
44
+ console.log(chalk_1.default.green('\nSetup complete! You can change these anytime using "enver config".\n'));
45
+ }
46
+ program
47
+ .command('login')
48
+ .description('Authenticate with your Google account and run setup')
49
+ .action(async () => {
50
+ try {
51
+ await (0, auth_1.getAuthenticatedClient)();
52
+ console.log(chalk_1.default.green('Successfully authenticated!'));
53
+ await runSetup();
54
+ }
55
+ catch (error) {
56
+ console.error(chalk_1.default.red('Authentication failed:'), error.message);
57
+ }
58
+ });
59
+ program
60
+ .command('logout')
61
+ .description('Sign out and remove authentication tokens')
62
+ .action(async () => {
63
+ await (0, auth_1.logout)();
64
+ console.log(chalk_1.default.yellow('Successfully logged out. Credentials removed locally.'));
65
+ });
66
+ program
67
+ .command('push')
68
+ .description('Upload local .env (encrypted) to Google Drive')
69
+ .option('-f, --file <filename>', 'Local filename to backup (default: .env)')
70
+ .action(async (options) => {
71
+ await (0, drive_1.uploadEnv)({ file: options.file });
72
+ });
73
+ program
74
+ .command('pull')
75
+ .description('Restore .env from Google Drive with diff preview')
76
+ .option('-f, --file <filename>', 'Local filename to restore to (default: .env)')
77
+ .option('-n, --name <project>', 'Specific project folder name')
78
+ .option('-l, --latest', 'Automatically pick the latest backup')
79
+ .action(async (options) => {
80
+ await (0, drive_1.downloadEnv)({
81
+ file: options.file,
82
+ name: options.name,
83
+ latest: options.latest
84
+ });
85
+ });
86
+ const config = program.command('config').description('Manage CLI configuration');
87
+ config
88
+ .command('password')
89
+ .description('Set, update, or remove the default encryption password')
90
+ .action(async () => {
91
+ const { action } = await inquirer_1.default.prompt([{
92
+ type: 'list',
93
+ name: 'action',
94
+ message: 'What would you like to do?',
95
+ choices: ['Set/Update Password', 'Remove Password', 'Cancel'],
96
+ }]);
97
+ if (action === 'Set/Update Password') {
98
+ const { password } = await inquirer_1.default.prompt([{
99
+ type: 'password',
100
+ name: 'password',
101
+ message: 'Enter new default password:',
102
+ mask: '*',
103
+ }]);
104
+ await (0, config_1.setPassword)(password);
105
+ console.log(chalk_1.default.green('Default password updated!'));
106
+ }
107
+ else if (action === 'Remove Password') {
108
+ await (0, config_1.setPassword)(undefined);
109
+ console.log(chalk_1.default.yellow('Default password removed. You will be prompted manually for future operations.'));
110
+ }
111
+ });
112
+ config
113
+ .command('encryption <state>')
114
+ .description('Enable or disable encryption globally (on|off)')
115
+ .action(async (state) => {
116
+ if (state === 'on') {
117
+ await (0, config_1.setEncryption)(true);
118
+ console.log(chalk_1.default.green('Encryption enabled! Local files will be encrypted before upload.'));
119
+ }
120
+ else if (state === 'off') {
121
+ await (0, config_1.setEncryption)(false);
122
+ console.log(chalk_1.default.yellow('Encryption disabled! Backups will be stored as plain text.'));
123
+ }
124
+ else {
125
+ console.error(chalk_1.default.red('Invalid state. Use "on" or "off".'));
126
+ }
127
+ });
128
+ config
129
+ .command('reset')
130
+ .description('Reset all global configurations to factory defaults')
131
+ .action(async () => {
132
+ const { confirm } = await inquirer_1.default.prompt([{
133
+ type: 'confirm',
134
+ name: 'confirm',
135
+ message: chalk_1.default.red('Are you sure you want to reset all configurations? This cannot be undone.'),
136
+ default: false,
137
+ }]);
138
+ if (confirm) {
139
+ await (0, config_1.resetConfig)();
140
+ console.log(chalk_1.default.green('Configuration reset successfully! Settings are back to factory defaults.'));
141
+ }
142
+ });
143
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "enver-cli",
3
+ "version": "1.0.0",
4
+ "description": "A CLI tool to backup and restore .env files using Google Drive",
5
+ "main": "dist/bin/index.js",
6
+ "bin": {
7
+ "enver": "./dist/bin/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/bin/index.js",
12
+ "dev": "ts-node bin/index.ts"
13
+ },
14
+ "keywords": [
15
+ "cli",
16
+ "env",
17
+ "google-drive",
18
+ "backup",
19
+ "enver",
20
+ "env-manager",
21
+ "env-manager-cli",
22
+ "enver-cli"
23
+ ],
24
+ "author": "",
25
+ "license": "ISC",
26
+ "dependencies": {
27
+ "chalk": "^4.1.2",
28
+ "commander": "^11.1.0",
29
+ "diff": "^5.2.0",
30
+ "dotenv": "^16.4.1",
31
+ "fs-extra": "^11.2.0",
32
+ "googleapis": "^131.0.0",
33
+ "inquirer": "^8.2.6",
34
+ "open": "^8.4.2",
35
+ "ora": "^5.4.1"
36
+ },
37
+ "devDependencies": {
38
+ "@types/diff": "^5.0.9",
39
+ "@types/fs-extra": "^11.0.4",
40
+ "@types/inquirer": "^8.2.10",
41
+ "@types/node": "^20.11.16",
42
+ "ts-node": "^10.9.2",
43
+ "typescript": "^5.3.3"
44
+ }
45
+ }