@dotenvx/dotenvx 2.4.1 → 2.6.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,19 @@
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/v2.4.1...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.6.0...main)
6
+
7
+ ## [2.6.0](https://github.com/dotenvx/dotenvx/compare/v2.5.0...v2.6.0) (2026-07-11)
8
+
9
+ ### Added
10
+
11
+ * Support `native up|down|pull|push` for windows ([#834](https://github.com/dotenvx/dotenvx/pull/884))
12
+
13
+ ## [2.5.0](https://github.com/dotenvx/dotenvx/compare/v2.4.2...v2.5.0) (2026-07-11)
14
+
15
+ ### Added
16
+
17
+ * Support `native up|down|pull|push` for windows ([#833](https://github.com/dotenvx/dotenvx/pull/883))
6
18
 
7
19
  ## [2.4.1](https://github.com/dotenvx/dotenvx/compare/v2.4.0...v2.4.1) (2026-07-10)
8
20
 
package/README.md CHANGED
@@ -2482,9 +2482,11 @@ $ dotenvx lock down
2482
2482
  </details>
2483
2483
  <details><summary>`native`</summary><br>
2484
2484
 
2485
- Move private keys into your OS secret store (macOS Keychain supported).
2485
+ Move private keys into your OS secret store.
2486
2486
 
2487
- Currently supported on macOS.
2487
+ Native commands support macOS Keychain, Linux Secret Service, and Windows Credential Manager.
2488
+
2489
+ On Linux, `secret-tool` and an available Secret Service are required.
2488
2490
 
2489
2491
  </details>
2490
2492
  <details><summary>`native up`</summary><br>
@@ -2646,7 +2648,7 @@ Commands:
2646
2648
 
2647
2649
  Professional Security:
2648
2650
  lock ⊡ lock private keys with a local passphrase
2649
- native ⌥ move private keys into your OS secret store (macOS Keychain supported)
2651
+ native ⌥ move private keys into your OS secret store
2650
2652
  armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]
2651
2653
  ```
2652
2654
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.4.1",
2
+ "version": "2.6.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a secure dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -1,6 +1,6 @@
1
1
  function configureNativeCommand (native) {
2
2
  native
3
- .description('move private keys into your OS secret store (macOS Keychain supported)')
3
+ .description('move private keys into your OS secret store')
4
4
  .action(function () {
5
5
  this.help()
6
6
  })
@@ -260,7 +260,7 @@ program.command('help [command]')
260
260
  program.addHelpText('after', ' ')
261
261
  program.addHelpText('after', 'Professional Security: ')
262
262
  program.addHelpText('after', ' lock ⊡ lock private keys with a local passphrase')
263
- program.addHelpText('after', ' native ⌥ move private keys into your OS secret store (macOS Keychain supported)')
263
+ program.addHelpText('after', ' native ⌥ move private keys into your OS secret store')
264
264
  program.addHelpText('after', ' armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]')
265
265
 
266
266
  // dotenvx native
@@ -0,0 +1,43 @@
1
+ const { execFileSync } = require('child_process')
2
+
3
+ const SECRET_TOOL_BIN = 'secret-tool'
4
+ const SERVICE = 'dotenvx'
5
+
6
+ function attributes (publicKey) {
7
+ return ['service', SERVICE, 'public-key', publicKey]
8
+ }
9
+
10
+ function findGenericPassword (publicKey) {
11
+ try {
12
+ return execFileSync(SECRET_TOOL_BIN, ['lookup', ...attributes(publicKey)], {
13
+ encoding: 'utf8',
14
+ stdio: ['ignore', 'pipe', 'pipe']
15
+ }).trim() || null
16
+ } catch {
17
+ throw new Error('failed to read private key from Linux Secret Service')
18
+ }
19
+ }
20
+
21
+ function addGenericPassword (publicKey, label, privateKey) {
22
+ try {
23
+ execFileSync(SECRET_TOOL_BIN, ['store', `--label=${label}`, ...attributes(publicKey)], {
24
+ input: privateKey,
25
+ encoding: 'utf8',
26
+ stdio: ['pipe', 'ignore', 'pipe']
27
+ })
28
+ } catch {
29
+ throw new Error('failed to save private key to Linux Secret Service')
30
+ }
31
+ }
32
+
33
+ function deleteGenericPassword (publicKey) {
34
+ try {
35
+ execFileSync(SECRET_TOOL_BIN, ['clear', ...attributes(publicKey)], {
36
+ stdio: ['ignore', 'ignore', 'pipe']
37
+ })
38
+ } catch {
39
+ throw new Error('failed to delete private key from Linux Secret Service')
40
+ }
41
+ }
42
+
43
+ module.exports = { findGenericPassword, addGenericPassword, deleteGenericPassword }
@@ -0,0 +1,161 @@
1
+ const { execFileSync } = require('child_process')
2
+
3
+ const POWERSHELL_BIN = 'powershell.exe'
4
+ const SERVICE = 'dotenvx'
5
+
6
+ const script = `
7
+ $ErrorActionPreference = 'Stop'
8
+
9
+ Add-Type -TypeDefinition @'
10
+ using System;
11
+ using System.ComponentModel;
12
+ using System.Runtime.InteropServices;
13
+ using System.Text;
14
+
15
+ public static class DotenvxCredentialManager
16
+ {
17
+ private const uint CRED_TYPE_GENERIC = 1;
18
+ private const uint CRED_PERSIST_LOCAL_MACHINE = 2;
19
+ private const int ERROR_NOT_FOUND = 1168;
20
+
21
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
22
+ private struct Credential
23
+ {
24
+ public uint Flags;
25
+ public uint Type;
26
+ public string TargetName;
27
+ public string Comment;
28
+ public long LastWritten;
29
+ public uint CredentialBlobSize;
30
+ public IntPtr CredentialBlob;
31
+ public uint Persist;
32
+ public uint AttributeCount;
33
+ public IntPtr Attributes;
34
+ public string TargetAlias;
35
+ public string UserName;
36
+ }
37
+
38
+ [DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
39
+ private static extern bool CredWrite(ref Credential credential, uint flags);
40
+
41
+ [DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
42
+ private static extern bool CredRead(string target, uint type, uint flags, out IntPtr credentialPtr);
43
+
44
+ [DllImport("Advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)]
45
+ private static extern bool CredDelete(string target, uint type, uint flags);
46
+
47
+ [DllImport("Advapi32.dll", SetLastError = true)]
48
+ private static extern void CredFree(IntPtr credentialPtr);
49
+
50
+ public static string Read(string target)
51
+ {
52
+ IntPtr credentialPtr;
53
+ if (!CredRead(target, CRED_TYPE_GENERIC, 0, out credentialPtr))
54
+ {
55
+ int error = Marshal.GetLastWin32Error();
56
+ if (error == ERROR_NOT_FOUND) return null;
57
+ throw new Win32Exception(error);
58
+ }
59
+
60
+ try
61
+ {
62
+ Credential credential = (Credential)Marshal.PtrToStructure(credentialPtr, typeof(Credential));
63
+ if (credential.CredentialBlob == IntPtr.Zero || credential.CredentialBlobSize == 0) return null;
64
+ return Marshal.PtrToStringUni(credential.CredentialBlob, (int)credential.CredentialBlobSize / 2);
65
+ }
66
+ finally
67
+ {
68
+ CredFree(credentialPtr);
69
+ }
70
+ }
71
+
72
+ public static void Write(string target, string username, string secret)
73
+ {
74
+ IntPtr secretPtr = Marshal.StringToCoTaskMemUni(secret);
75
+ try
76
+ {
77
+ Credential credential = new Credential();
78
+ credential.Type = CRED_TYPE_GENERIC;
79
+ credential.TargetName = target;
80
+ credential.CredentialBlobSize = (uint)Encoding.Unicode.GetByteCount(secret);
81
+ credential.CredentialBlob = secretPtr;
82
+ credential.Persist = CRED_PERSIST_LOCAL_MACHINE;
83
+ credential.UserName = username;
84
+
85
+ if (!CredWrite(ref credential, 0))
86
+ {
87
+ throw new Win32Exception(Marshal.GetLastWin32Error());
88
+ }
89
+ }
90
+ finally
91
+ {
92
+ Marshal.ZeroFreeCoTaskMemUnicode(secretPtr);
93
+ }
94
+ }
95
+
96
+ public static void Delete(string target)
97
+ {
98
+ if (!CredDelete(target, CRED_TYPE_GENERIC, 0))
99
+ {
100
+ throw new Win32Exception(Marshal.GetLastWin32Error());
101
+ }
102
+ }
103
+ }
104
+ '@
105
+
106
+ $payload = [Console]::In.ReadToEnd() | ConvertFrom-Json
107
+
108
+ if ($payload.action -eq 'read') {
109
+ $secret = [DotenvxCredentialManager]::Read([string]$payload.target)
110
+ if ($null -ne $secret) {
111
+ [Console]::Out.Write($secret)
112
+ }
113
+ } elseif ($payload.action -eq 'write') {
114
+ [DotenvxCredentialManager]::Write([string]$payload.target, [string]$payload.username, [string]$payload.secret)
115
+ } elseif ($payload.action -eq 'delete') {
116
+ [DotenvxCredentialManager]::Delete([string]$payload.target)
117
+ } else {
118
+ throw "unsupported credential action"
119
+ }
120
+ `
121
+
122
+ const encodedScript = Buffer.from(script, 'utf16le').toString('base64')
123
+
124
+ function target (publicKey) {
125
+ return `${SERVICE}:${publicKey}`
126
+ }
127
+
128
+ function run (payload) {
129
+ return execFileSync(POWERSHELL_BIN, ['-NoProfile', '-NonInteractive', '-EncodedCommand', encodedScript], {
130
+ input: JSON.stringify(payload),
131
+ encoding: 'utf8',
132
+ stdio: ['pipe', 'pipe', 'pipe'],
133
+ windowsHide: true
134
+ })
135
+ }
136
+
137
+ function findGenericPassword (publicKey) {
138
+ try {
139
+ return run({ action: 'read', target: target(publicKey) }).trim() || null
140
+ } catch {
141
+ throw new Error('failed to read private key from Windows Credential Manager')
142
+ }
143
+ }
144
+
145
+ function addGenericPassword (publicKey, privateKey) {
146
+ try {
147
+ run({ action: 'write', target: target(publicKey), username: publicKey, secret: privateKey })
148
+ } catch {
149
+ throw new Error('failed to save private key to Windows Credential Manager')
150
+ }
151
+ }
152
+
153
+ function deleteGenericPassword (publicKey) {
154
+ try {
155
+ run({ action: 'delete', target: target(publicKey) })
156
+ } catch {
157
+ throw new Error('failed to delete private key from Windows Credential Manager')
158
+ }
159
+ }
160
+
161
+ module.exports = { findGenericPassword, addGenericPassword, deleteGenericPassword }
@@ -1,7 +1,7 @@
1
1
  const Session = require('./../../db/session')
2
2
 
3
3
  const armorProvider = require('./armor/index')
4
- const keychainProvider = require('./keychain/index')
4
+ const nativeProvider = require('./native/index')
5
5
 
6
6
  function syncArmorProvider (publicKeyHex) {
7
7
  const { createSyncFn } = require('@dotenvx/tooling')
@@ -43,8 +43,8 @@ function armorProviderForOptions (options) {
43
43
  })
44
44
  }
45
45
 
46
- function useKeychain (options) {
47
- if (process.platform !== 'darwin') return false
46
+ function useNative (options) {
47
+ if (!['darwin', 'linux', 'win32'].includes(process.platform)) return false
48
48
  if (process.env.CI) return false
49
49
  return options.noNative !== true && options.native !== false && options.noKeychain !== true
50
50
  }
@@ -67,8 +67,8 @@ async function providers (options = {}) {
67
67
 
68
68
  const providerFns = []
69
69
 
70
- if (useKeychain(options)) {
71
- providerFns.push(keychainProvider)
70
+ if (useNative(options)) {
71
+ providerFns.push(nativeProvider)
72
72
  }
73
73
 
74
74
  if (options.noArmor !== true && options.armor !== false) {
@@ -89,8 +89,8 @@ providers.sync = function providersSync (options = {}) {
89
89
 
90
90
  const providerFns = []
91
91
 
92
- if (useKeychain(options)) {
93
- providerFns.push(keychainProvider)
92
+ if (useNative(options)) {
93
+ providerFns.push(nativeProvider)
94
94
  }
95
95
 
96
96
  if (options.noArmor !== true && options.armor !== false) {
@@ -0,0 +1,34 @@
1
+ const { execFileSync } = require('child_process')
2
+
3
+ const windowsCredentialManager = require('../../helpers/windowsCredentialManager')
4
+ const linuxSecretService = require('../../helpers/linuxSecretService')
5
+
6
+ const SECURITY_BIN = '/usr/bin/security'
7
+ const SERVICE = 'dotenvx'
8
+
9
+ function findMacosPrivateKey (publicKeyHex) {
10
+ return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKeyHex, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
11
+ }
12
+
13
+ function index (publicKeyHex) {
14
+ if (!['darwin', 'linux', 'win32'].includes(process.platform)) return {}
15
+
16
+ try {
17
+ let privateKeyHex
18
+ if (process.platform === 'win32') {
19
+ privateKeyHex = windowsCredentialManager.findGenericPassword(publicKeyHex)
20
+ } else if (process.platform === 'linux') {
21
+ privateKeyHex = linuxSecretService.findGenericPassword(publicKeyHex)
22
+ } else {
23
+ privateKeyHex = findMacosPrivateKey(publicKeyHex)
24
+ }
25
+
26
+ if (!privateKeyHex) return {}
27
+
28
+ return { [publicKeyHex]: privateKeyHex }
29
+ } catch {
30
+ return {}
31
+ }
32
+ }
33
+
34
+ module.exports = index
@@ -3,10 +3,39 @@ const { execFileSync } = require('child_process')
3
3
  const keynames = require('../conventions/keynames')
4
4
  const readEnvKey = require('../helpers/readEnvKey')
5
5
  const upsertEnvKey = require('../helpers/upsertEnvKey')
6
+ const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
7
+ const windowsCredentialManager = require('../helpers/windowsCredentialManager')
8
+ const linuxSecretService = require('../helpers/linuxSecretService')
6
9
 
7
10
  const SECURITY_BIN = '/usr/bin/security'
8
11
  const SERVICE = 'dotenvx'
9
12
 
13
+ function findGenericPassword (publicKey) {
14
+ if (process.platform === 'win32') {
15
+ return windowsCredentialManager.findGenericPassword(publicKey)
16
+ }
17
+
18
+ if (process.platform === 'linux') {
19
+ return linuxSecretService.findGenericPassword(publicKey)
20
+ }
21
+
22
+ return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
23
+ }
24
+
25
+ function deleteGenericPassword (publicKey) {
26
+ if (process.platform === 'win32') {
27
+ windowsCredentialManager.deleteGenericPassword(publicKey)
28
+ return
29
+ }
30
+
31
+ if (process.platform === 'linux') {
32
+ linuxSecretService.deleteGenericPassword(publicKey)
33
+ return
34
+ }
35
+
36
+ execFileSync(SECURITY_BIN, ['delete-generic-password', '-s', SERVICE, '-a', publicKey], { stdio: 'ignore' })
37
+ }
38
+
10
39
  class KeychainDown {
11
40
  constructor (envFile = '.env', envKeysFile = '.env.keys') {
12
41
  this.envFile = envFile
@@ -26,7 +55,13 @@ class KeychainDown {
26
55
  let privateKey
27
56
 
28
57
  try {
29
- privateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
58
+ privateKey = findGenericPassword(publicKey)
59
+ if (!privateKey) {
60
+ const secretStore = process.platform === 'win32'
61
+ ? 'Windows Credential Manager'
62
+ : process.platform === 'linux' ? 'Linux Secret Service' : 'macOS Keychain'
63
+ throw new Error(`[NOT_FOUND] private key not found in ${secretStore} (${armoredKeyDisplay(publicKey)}). fix: [dotenvx native up]`)
64
+ }
30
65
  } catch (error) {
31
66
  privateKey = readEnvKey(privateKeyName, envKeysFile, { strict: false })
32
67
 
@@ -43,7 +78,7 @@ class KeychainDown {
43
78
  }
44
79
 
45
80
  upsertEnvKey(privateKeyName, privateKey, envKeysFile)
46
- execFileSync(SECURITY_BIN, ['delete-generic-password', '-s', SERVICE, '-a', publicKey], { stdio: 'ignore' })
81
+ deleteGenericPassword(publicKey)
47
82
 
48
83
  return {
49
84
  changed: true,
@@ -4,10 +4,24 @@ const keynames = require('../conventions/keynames')
4
4
  const readEnvKey = require('../helpers/readEnvKey')
5
5
  const upsertEnvKey = require('../helpers/upsertEnvKey')
6
6
  const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
7
+ const windowsCredentialManager = require('../helpers/windowsCredentialManager')
8
+ const linuxSecretService = require('../helpers/linuxSecretService')
7
9
 
8
10
  const SECURITY_BIN = '/usr/bin/security'
9
11
  const SERVICE = 'dotenvx'
10
12
 
13
+ function findGenericPassword (publicKey) {
14
+ if (process.platform === 'win32') {
15
+ return windowsCredentialManager.findGenericPassword(publicKey)
16
+ }
17
+
18
+ if (process.platform === 'linux') {
19
+ return linuxSecretService.findGenericPassword(publicKey)
20
+ }
21
+
22
+ return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
23
+ }
24
+
11
25
  class KeychainPull {
12
26
  constructor (envFile = '.env', envKeysFile = '.env.keys') {
13
27
  this.envFile = envFile
@@ -27,9 +41,13 @@ class KeychainPull {
27
41
  let privateKey
28
42
 
29
43
  try {
30
- privateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
44
+ privateKey = findGenericPassword(publicKey)
45
+ if (!privateKey) throw new Error('not found')
31
46
  } catch {
32
- throw new Error(`[NOT_FOUND] private key not found in macOS Keychain (${armoredKeyDisplay(publicKey)}). fix: [dotenvx native up]`)
47
+ const secretStore = process.platform === 'win32'
48
+ ? 'Windows Credential Manager'
49
+ : process.platform === 'linux' ? 'Linux Secret Service' : 'macOS Keychain'
50
+ throw new Error(`[NOT_FOUND] private key not found in ${secretStore} (${armoredKeyDisplay(publicKey)}). fix: [dotenvx native up]`)
33
51
  }
34
52
 
35
53
  const result = upsertEnvKey(privateKeyName, privateKey, envKeysFile)
@@ -3,11 +3,23 @@ const { execFileSync } = require('child_process')
3
3
  const keynames = require('../conventions/keynames')
4
4
  const readEnvKey = require('../helpers/readEnvKey')
5
5
  const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
6
+ const windowsCredentialManager = require('../helpers/windowsCredentialManager')
7
+ const linuxSecretService = require('../helpers/linuxSecretService')
6
8
 
7
9
  const SECURITY_BIN = '/usr/bin/security'
8
10
  const SERVICE = 'dotenvx'
9
11
 
10
12
  function addGenericPassword (publicKey, label, privateKey) {
13
+ if (process.platform === 'win32') {
14
+ windowsCredentialManager.addGenericPassword(publicKey, privateKey)
15
+ return
16
+ }
17
+
18
+ if (process.platform === 'linux') {
19
+ linuxSecretService.addGenericPassword(publicKey, label, privateKey)
20
+ return
21
+ }
22
+
11
23
  try {
12
24
  execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', publicKey, '-l', label, '-w', privateKey], { stdio: 'ignore' })
13
25
  } catch {
@@ -15,6 +27,18 @@ function addGenericPassword (publicKey, label, privateKey) {
15
27
  }
16
28
  }
17
29
 
30
+ function findGenericPassword (publicKey) {
31
+ if (process.platform === 'win32') {
32
+ return windowsCredentialManager.findGenericPassword(publicKey)
33
+ }
34
+
35
+ if (process.platform === 'linux') {
36
+ return linuxSecretService.findGenericPassword(publicKey)
37
+ }
38
+
39
+ return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
40
+ }
41
+
18
42
  class KeychainPush {
19
43
  constructor (envFile = '.env', envKeysFile = '.env.keys') {
20
44
  this.envFile = envFile
@@ -35,7 +59,7 @@ class KeychainPush {
35
59
  const label = `dotenvx (${armoredKeyDisplay(publicKey)})`
36
60
 
37
61
  try {
38
- const existingPrivateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
62
+ const existingPrivateKey = findGenericPassword(publicKey)
39
63
 
40
64
  if (existingPrivateKey === privateKey) {
41
65
  return {
@@ -4,11 +4,23 @@ const keynames = require('../conventions/keynames')
4
4
  const readEnvKey = require('../helpers/readEnvKey')
5
5
  const removeEnvKey = require('../helpers/removeEnvKey')
6
6
  const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
7
+ const windowsCredentialManager = require('../helpers/windowsCredentialManager')
8
+ const linuxSecretService = require('../helpers/linuxSecretService')
7
9
 
8
10
  const SECURITY_BIN = '/usr/bin/security'
9
11
  const SERVICE = 'dotenvx'
10
12
 
11
13
  function addGenericPassword (publicKey, label, privateKey) {
14
+ if (process.platform === 'win32') {
15
+ windowsCredentialManager.addGenericPassword(publicKey, privateKey)
16
+ return
17
+ }
18
+
19
+ if (process.platform === 'linux') {
20
+ linuxSecretService.addGenericPassword(publicKey, label, privateKey)
21
+ return
22
+ }
23
+
12
24
  try {
13
25
  execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', publicKey, '-l', label, '-w', privateKey], { stdio: 'ignore' })
14
26
  } catch {
@@ -16,6 +28,18 @@ function addGenericPassword (publicKey, label, privateKey) {
16
28
  }
17
29
  }
18
30
 
31
+ function findGenericPassword (publicKey) {
32
+ if (process.platform === 'win32') {
33
+ return windowsCredentialManager.findGenericPassword(publicKey)
34
+ }
35
+
36
+ if (process.platform === 'linux') {
37
+ return linuxSecretService.findGenericPassword(publicKey)
38
+ }
39
+
40
+ return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
41
+ }
42
+
19
43
  class KeychainUp {
20
44
  constructor (envFile = '.env', envKeysFile = '.env.keys') {
21
45
  this.envFile = envFile
@@ -36,7 +60,7 @@ class KeychainUp {
36
60
  let existingPrivateKey
37
61
 
38
62
  try {
39
- existingPrivateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
63
+ existingPrivateKey = findGenericPassword(publicKey)
40
64
  } catch {}
41
65
 
42
66
  const privateKey = readEnvKey(privateKeyName, envKeysFile, { strict: !existingPrivateKey })
@@ -1,20 +0,0 @@
1
- const { execFileSync } = require('child_process')
2
-
3
- const SECURITY_BIN = '/usr/bin/security'
4
- const SERVICE = 'dotenvx'
5
-
6
- function index (publicKeyHex) {
7
- if (process.platform !== 'darwin') return {}
8
-
9
- try {
10
- const stdout = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKeyHex, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
11
- const privateKeyHex = stdout.trim()
12
- if (!privateKeyHex) return {}
13
-
14
- return { [publicKeyHex]: privateKeyHex }
15
- } catch {
16
- return {}
17
- }
18
- }
19
-
20
- module.exports = index