@dotenvx/dotenvx 1.60.1 → 1.61.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/CHANGELOG.md CHANGED
@@ -2,7 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
- [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.60.1...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.61.0...main)
6
+
7
+ ## [1.61.0](https://github.com/dotenvx/dotenvx/compare/v1.60.2...v1.61.0) (2026-04-08)
8
+
9
+ ### Added
10
+
11
+ * Add `login` and `logout` method that proxy to `dotenvx-ops login/logout` ([#780](https://github.com/dotenvx/dotenvx/pull/780))
12
+ * Note: dotenvx continues to make zero outgoing HTTP requests and includes no telemetry. Outgoing requests occur only if you explicitly install the [dotenvx-ops](https://dotenvx.com/ops) SDK or CLI.
13
+
14
+ ## [1.60.2](https://github.com/dotenvx/dotenvx/compare/v1.60.1...v1.60.2) (2026-04-07)
15
+
16
+ ### Changed
17
+
18
+ * Communicate `local key` and `armored key` (for Ops stored keys) ([#778](https://github.com/dotenvx/dotenvx/pull/778))
6
19
 
7
20
  ## [1.60.1](https://github.com/dotenvx/dotenvx/compare/v1.60.0...v1.60.1) (2026-04-06)
8
21
 
package/README.md CHANGED
@@ -1572,7 +1572,7 @@ Encrypt the contents of a `.env` file to an encrypted `.env` file.
1572
1572
  $ echo "HELLO=Dotenvx" > .env
1573
1573
 
1574
1574
  $ dotenvx encrypt
1575
- ◈ encrypted (.env) + key (.env.keys)
1575
+ ◈ encrypted (.env) + local key (.env.keys)
1576
1576
  ⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
1577
1577
  ⮕ next run [DOTENV_PRIVATE_KEY='122...0b8' dotenvx run -- yourcommand] to test decryption locally
1578
1578
  ```
@@ -1587,7 +1587,7 @@ $ echo "HELLO=Dotenvx" > .env
1587
1587
  $ echo "HELLO=Production" > .env.production
1588
1588
 
1589
1589
  $ dotenvx encrypt -f .env.production
1590
- ◈ encrypted (.env.production) + key (.env.keys)
1590
+ ◈ encrypted (.env.production) + local key (.env.keys)
1591
1591
  ⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
1592
1592
  ⮕ next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally
1593
1593
  ```
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.60.1",
2
+ "version": "1.61.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "secrets for agents–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -57,10 +57,10 @@ async function encrypt () {
57
57
  let msg = `◈ encrypted (${changedFilepaths.join(',')})`
58
58
  if (localKeyAddedEnv) {
59
59
  const envKeysFilepath = localDisplayPath(localKeyAddedEnv.envKeysFilepath)
60
- msg += ` + key (${envKeysFilepath})`
60
+ msg += ` + local key (${envKeysFilepath})`
61
61
  }
62
62
  if (remoteKeyAddedEnv) {
63
- msg += ' + key ⛨'
63
+ msg += ' + armored key ⛨'
64
64
  }
65
65
  logger.success(msg)
66
66
  } else if (unchangedFilepaths.length > 0) {
@@ -64,10 +64,10 @@ async function rotate () {
64
64
  let msg = `⟳ rotated (${changedFilepaths.join(',')})`
65
65
  if (localKeyAddedEnv) {
66
66
  const envKeysFilepath = localDisplayPath(localKeyAddedEnv.envKeysFilepath)
67
- msg += ` + key (${envKeysFilepath})`
67
+ msg += ` + local key (${envKeysFilepath})`
68
68
  }
69
69
  if (remoteKeyAddedEnv) {
70
- msg += ' + key ⛨'
70
+ msg += ' + armored key ⛨'
71
71
  }
72
72
  logger.success(msg)
73
73
  } else if (unchangedFilepaths.length > 0) {
@@ -59,10 +59,10 @@ async function set (key, value) {
59
59
  const remoteKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.remotePrivateKeyAdded)
60
60
  let keyAddedSuffix = ''
61
61
  if (localKeyAddedEnv) {
62
- keyAddedSuffix = ` + key (${localDisplayPath(localKeyAddedEnv.envKeysFilepath)})`
62
+ keyAddedSuffix = ` + local key (${localDisplayPath(localKeyAddedEnv.envKeysFilepath)})`
63
63
  }
64
64
  if (remoteKeyAddedEnv) {
65
- keyAddedSuffix = ' + key ⛨'
65
+ keyAddedSuffix = ' + armored key ⛨'
66
66
  }
67
67
 
68
68
  if (spinner) spinner.stop()
@@ -190,6 +190,23 @@ program.command('ls')
190
190
  .option('-ef, --exclude-env-file <excludeFilenames...>', 'path(s) to exclude from your env file(s) (default: none)')
191
191
  .action(lsAction)
192
192
 
193
+ // dotenvx login
194
+ program.command('login')
195
+ .description('log in to unlock ⛨ ARMORED KEYS ✦ BETA')
196
+ .action(() => {
197
+ const rawArgs = ['ops', 'login', ...process.argv.slice(3)]
198
+ executeDynamic(program, 'ops', rawArgs)
199
+ })
200
+
201
+ // dotenvx logout
202
+ program.command('logout', { hidden: true })
203
+ .description('optional: log out of your dotenvx account')
204
+ .allowUnknownOption()
205
+ .action(() => {
206
+ const rawArgs = ['ops', 'logout', ...process.argv.slice(3)]
207
+ executeDynamic(program, 'ops', rawArgs)
208
+ })
209
+
193
210
  // dotenvx help
194
211
  program.command('help [command]')
195
212
  .description('display help for command')
@@ -18,7 +18,7 @@ function opsBanner (installCommand) {
18
18
  '',
19
19
  ' ⛨ ARMORED KEYS: Harden your private keys.',
20
20
  ` ⮕ install [${installCommand}]`,
21
- ' ⮕ and then run [dotenvx-ops login]'
21
+ ' ⮕ then run [dotenvx-ops login]'
22
22
  ]
23
23
 
24
24
  const innerWidth = Math.max(67, ...lines.map((line) => line.length))
@@ -5,6 +5,37 @@ function removeOptionsHelpParts (lines) {
5
5
  lines[i] = lines[i].replace(' [options]', '')
6
6
  }
7
7
 
8
+ let commandsStart = -1
9
+ for (let i = 0; i < lines.length; i++) {
10
+ if (lines[i] === 'Commands:') {
11
+ commandsStart = i + 1
12
+ break
13
+ }
14
+ }
15
+
16
+ if (commandsStart !== -1) {
17
+ const commands = []
18
+
19
+ for (let i = commandsStart; i < lines.length; i++) {
20
+ const line = lines[i]
21
+ if (line === '' || line.endsWith(':')) {
22
+ break
23
+ }
24
+
25
+ const match = line.match(/^(\s{2})(\S(?:.*\S)?)\s{2,}(\S.*)$/)
26
+ if (match) {
27
+ commands.push({ index: i, indent: match[1], command: match[2], description: match[3] })
28
+ }
29
+ }
30
+
31
+ if (commands.length > 0) {
32
+ const maxCommandLength = commands.reduce((max, item) => Math.max(max, item.command.length), 0)
33
+ for (const item of commands) {
34
+ lines[item.index] = `${item.indent}${item.command.padEnd(maxCommandLength + 2)}${item.description}`
35
+ }
36
+ }
37
+ }
38
+
8
39
  return lines
9
40
  }
10
41
 
package/src/lib/main.js CHANGED
@@ -199,10 +199,10 @@ const set = function (key, value, options = {}) {
199
199
  const remoteKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.remotePrivateKeyAdded)
200
200
 
201
201
  if (localKeyAddedEnv) {
202
- keyAddedSuffix = ` + key (${localDisplayPath(localKeyAddedEnv.envKeysFilepath)})`
202
+ keyAddedSuffix = ` + local key (${localDisplayPath(localKeyAddedEnv.envKeysFilepath)})`
203
203
  }
204
204
  if (remoteKeyAddedEnv) {
205
- keyAddedSuffix = ' + key ⛨'
205
+ keyAddedSuffix = ' + armored key ⛨'
206
206
  }
207
207
 
208
208
  if (changedFilepaths.length > 0) {