@dotenvx/dotenvx 1.71.3 → 1.73.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +3 -3
  3. package/package.json +8 -4
  4. package/src/cli/actions/armor/down.js +37 -0
  5. package/src/cli/actions/armor/move.js +42 -0
  6. package/src/cli/actions/armor/pull.js +37 -0
  7. package/src/cli/actions/armor/push.js +37 -0
  8. package/src/cli/actions/armor/up.js +37 -0
  9. package/src/cli/actions/decrypt.js +1 -1
  10. package/src/cli/actions/encrypt.js +1 -1
  11. package/src/cli/actions/login.js +63 -0
  12. package/src/cli/actions/logout.js +36 -0
  13. package/src/cli/actions/normalizeArmorOptions.js +1 -2
  14. package/src/cli/actions/set.js +1 -1
  15. package/src/cli/commands/armor.js +72 -0
  16. package/src/cli/dotenvx.js +17 -19
  17. package/src/db/device.js +73 -0
  18. package/src/db/session.js +187 -4
  19. package/src/lib/api/getAccount.js +32 -0
  20. package/src/lib/api/postArmorDown.js +48 -0
  21. package/src/lib/api/postArmorMove.js +48 -0
  22. package/src/lib/api/postArmorPull.js +48 -0
  23. package/src/lib/api/postArmorPush.js +48 -0
  24. package/src/lib/api/postArmorUp.js +51 -0
  25. package/src/lib/api/postKeypair.js +60 -0
  26. package/src/lib/api/postLogout.js +34 -0
  27. package/src/lib/api/postOauthDeviceCode.js +38 -0
  28. package/src/lib/api/postOauthToken.js +35 -0
  29. package/src/lib/helpers/armoredKeyDisplay.js +10 -0
  30. package/src/lib/helpers/buildApiError.js +16 -0
  31. package/src/lib/helpers/buildOauthError.js +14 -0
  32. package/src/lib/helpers/createSpinner.js +1 -1
  33. package/src/lib/helpers/cryptography/armorKeypair.js +32 -11
  34. package/src/lib/helpers/cryptography/armorKeypairSync.js +48 -7
  35. package/src/lib/helpers/cryptography/provision.js +2 -2
  36. package/src/lib/helpers/cryptography/provisionSync.js +2 -2
  37. package/src/lib/helpers/decryptDeviceValue.js +10 -0
  38. package/src/lib/helpers/encryptDeviceValue.js +9 -0
  39. package/src/lib/helpers/formatCode.js +11 -0
  40. package/src/lib/helpers/http.js +7 -0
  41. package/src/lib/helpers/jsonToEnv.js +7 -0
  42. package/src/lib/helpers/keyResolution/index.js +1 -1
  43. package/src/lib/helpers/keyResolution/{keyNames.js → keyNamesForEnvFile.js} +2 -2
  44. package/src/lib/helpers/keyResolution/keyValues.js +2 -2
  45. package/src/lib/helpers/keyResolution/keyValuesSync.js +2 -2
  46. package/src/lib/helpers/keypairMetadata.js +77 -0
  47. package/src/lib/helpers/listenForOpenKey.js +46 -0
  48. package/src/lib/helpers/normalizeToken.js +5 -0
  49. package/src/lib/helpers/openUrl.js +7 -0
  50. package/src/lib/helpers/readEnvKey.js +31 -0
  51. package/src/lib/helpers/removeEnvKey.js +50 -0
  52. package/src/lib/helpers/sanitizeCommandForMetadata.js +64 -0
  53. package/src/lib/helpers/teamChoicesFromMeta.js +8 -0
  54. package/src/lib/helpers/upsertEnvKey.js +61 -0
  55. package/src/lib/main.d.ts +0 -24
  56. package/src/lib/main.js +1 -1
  57. package/src/lib/services/armorDown.js +71 -0
  58. package/src/lib/services/armorKeypair.js +156 -0
  59. package/src/lib/services/armorMove.js +54 -0
  60. package/src/lib/services/armorPull.js +71 -0
  61. package/src/lib/services/armorPush.js +76 -0
  62. package/src/lib/services/armorUp.js +73 -0
  63. package/src/lib/services/decrypt.js +2 -2
  64. package/src/lib/services/encrypt.js +2 -2
  65. package/src/lib/services/keypair.js +5 -7
  66. package/src/lib/services/login.js +26 -0
  67. package/src/lib/services/loginPoll.js +35 -0
  68. package/src/lib/services/logout.js +26 -0
  69. package/src/lib/services/rotate.js +2 -2
  70. package/src/lib/services/run.js +3 -3
  71. package/src/lib/services/sets.js +3 -3
  72. package/src/lib/extensions/armor.js +0 -204
@@ -0,0 +1,26 @@
1
+ const Session = require('../../db/session')
2
+ const PostOauthDeviceCode = require('../api/postOauthDeviceCode')
3
+
4
+ class Login {
5
+ constructor (hostname) {
6
+ this.hostname = hostname
7
+ }
8
+
9
+ async run () {
10
+ const sesh = new Session()
11
+ const devicePublicKey = sesh.devicePublicKey()
12
+ const systemInformation = await sesh.systemInformation()
13
+
14
+ const data = await new PostOauthDeviceCode(this.hostname, devicePublicKey, systemInformation).run()
15
+
16
+ return {
17
+ deviceCode: data.device_code,
18
+ userCode: data.user_code,
19
+ verificationUri: data.verification_uri,
20
+ verificationUriComplete: data.verification_uri_complete,
21
+ interval: data.interval
22
+ }
23
+ }
24
+ }
25
+
26
+ module.exports = Login
@@ -0,0 +1,35 @@
1
+ const Session = require('../../db/session')
2
+ const PostOauthToken = require('../api/postOauthToken')
3
+
4
+ class LoginPoll {
5
+ constructor (hostname, deviceCode, interval) {
6
+ this.hostname = hostname
7
+ this.deviceCode = deviceCode
8
+ this.interval = interval
9
+ }
10
+
11
+ async run () {
12
+ const sesh = new Session()
13
+
14
+ while (true) {
15
+ try {
16
+ const data = await new PostOauthToken(this.hostname, this.deviceCode).run()
17
+
18
+ if (data.access_token) {
19
+ sesh.login(this.hostname, data.id, data.username, data.access_token)
20
+ return data
21
+ }
22
+
23
+ await new Promise(resolve => setTimeout(resolve, this.interval * 1000))
24
+ } catch (error) {
25
+ if (error.code === 'authorization_pending') {
26
+ await new Promise(resolve => setTimeout(resolve, (this.interval + 1) * 1000))
27
+ } else {
28
+ throw error
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ module.exports = LoginPoll
@@ -0,0 +1,26 @@
1
+ const Session = require('../../db/session')
2
+ const PostLogout = require('../api/postLogout')
3
+
4
+ class Logout {
5
+ constructor (hostname) {
6
+ this.hostname = hostname
7
+ }
8
+
9
+ async run () {
10
+ const sesh = new Session()
11
+ const token = sesh.token()
12
+
13
+ const data = await new PostLogout(this.hostname, token).run()
14
+
15
+ const id = data.id
16
+ const username = data.username
17
+ const accessToken = data.access_token
18
+ const settingsDevicesUrl = `${this.hostname}/settings/devices`
19
+
20
+ sesh.logout(this.hostname, id, accessToken)
21
+
22
+ return { username, accessToken, settingsDevicesUrl }
23
+ }
24
+ }
25
+
26
+ module.exports = Logout
@@ -11,7 +11,7 @@ const {
11
11
  } = require('./../helpers/envResolution')
12
12
 
13
13
  const {
14
- keyNames,
14
+ keyNamesForEnvFile,
15
15
  keyValues
16
16
  } = require('./../helpers/keyResolution')
17
17
 
@@ -83,7 +83,7 @@ class Rotate {
83
83
  let envSrc = await fsx.readFileX(filepath, { encoding })
84
84
  const envParsed = dotenvParse(envSrc, false, false, true)
85
85
 
86
- const { publicKeyName, privateKeyName } = keyNames(envFilepath)
86
+ const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
87
87
  const { privateKeyValue } = await keyValues(envFilepath, {
88
88
  keysFilepath: this.envKeysFilepath,
89
89
  noArmor: this.noArmor,
@@ -10,7 +10,7 @@ const detectEncoding = require('./../helpers/detectEncoding')
10
10
  const detectEncodingSync = require('./../helpers/detectEncodingSync')
11
11
 
12
12
  const {
13
- keyNames,
13
+ keyNamesForEnvFile,
14
14
  keyValues,
15
15
  keyValuesSync,
16
16
  keyValuesFromEnvSrc
@@ -147,7 +147,7 @@ class Run {
147
147
  const src = fsx.readFileXSync(filepath, { encoding })
148
148
  this.readableFilepaths.add(envFilepath)
149
149
 
150
- const { privateKeyName } = keyNames(filepath)
150
+ const { privateKeyName } = keyNamesForEnvFile(filepath)
151
151
  const { privateKeyValue, privateKeySource } = keyValuesSync(filepath, {
152
152
  keysFilepath: this.envKeysFilepath,
153
153
  noArmor: this.noArmor,
@@ -202,7 +202,7 @@ class Run {
202
202
  const src = await fsx.readFileX(filepath, { encoding })
203
203
  this.readableFilepaths.add(envFilepath)
204
204
 
205
- const { privateKeyName } = keyNames(filepath)
205
+ const { privateKeyName } = keyNamesForEnvFile(filepath)
206
206
  const { privateKeyValue, privateKeySource } = await keyValues(filepath, {
207
207
  keysFilepath: this.envKeysFilepath,
208
208
  noArmor: this.noArmor,
@@ -10,7 +10,7 @@ const {
10
10
  } = require('./../helpers/envResolution')
11
11
 
12
12
  const {
13
- keyNames,
13
+ keyNamesForEnvFile,
14
14
  keyValues,
15
15
  keyValuesSync
16
16
  } = require('./../helpers/keyResolution')
@@ -131,7 +131,7 @@ class Sets {
131
131
  let publicKey
132
132
  let privateKey
133
133
 
134
- const { publicKeyName, privateKeyName } = keyNames(filepath)
134
+ const { publicKeyName, privateKeyName } = keyNamesForEnvFile(filepath)
135
135
  const { publicKeyValue, privateKeyValue } = keyValuesSync(filepath, {
136
136
  keysFilepath: this.envKeysFilepath,
137
137
  noArmor: this.noArmor,
@@ -248,7 +248,7 @@ class Sets {
248
248
  let publicKey
249
249
  let privateKey
250
250
 
251
- const { publicKeyName, privateKeyName } = keyNames(filepath)
251
+ const { publicKeyName, privateKeyName } = keyNamesForEnvFile(filepath)
252
252
  const { publicKeyValue, privateKeyValue } = await keyValues(filepath, {
253
253
  keysFilepath: this.envKeysFilepath,
254
254
  noArmor: this.noArmor,
@@ -1,204 +0,0 @@
1
- const path = require('path')
2
- const childProcess = require('child_process')
3
- const util = require('util')
4
- const { logger } = require('../../shared/logger')
5
-
6
- const execFile = util.promisify(childProcess.execFile)
7
- const BINARY_NAMES = ['dotenvx-armor', 'dotenvx-vlt', 'dotenvx-ops']
8
-
9
- class Armor {
10
- async status () {
11
- if (this._isForcedOff()) return 'off'
12
-
13
- const binary = await this._resolveBinary()
14
- if (!binary) return 'off'
15
-
16
- try {
17
- return await this._exec(binary, ['status'])
18
- } catch (_e) {
19
- return 'off'
20
- }
21
- }
22
-
23
- statusSync () {
24
- if (this._isForcedOff()) return 'off'
25
-
26
- const binary = this._resolveBinarySync()
27
- if (!binary) return 'off'
28
-
29
- try {
30
- return this._execSync(binary, ['status'])
31
- } catch (_e) {
32
- return 'off'
33
- }
34
- }
35
-
36
- async keypair (publicKey, options = {}) {
37
- if (this._isForcedOff()) return {}
38
-
39
- const binary = await this._resolveBinary()
40
- if (!binary) return {}
41
-
42
- const args = ['keypair']
43
- if (options.noSpinner) args.push('--no-spinner')
44
- if (options.token) args.push('--token', options.token)
45
- if (options.envFilepath) args.push('-f', options.envFilepath)
46
- if (options.command) args.push('--metadata', this._serializeMetadata(options))
47
- if (publicKey) args.push(publicKey)
48
-
49
- try {
50
- return JSON.parse(await this._execInteractive(binary, args))
51
- } catch (_e) {
52
- return {}
53
- }
54
- }
55
-
56
- keypairSync (publicKey, options = {}) {
57
- if (this._isForcedOff()) return {}
58
-
59
- const binary = this._resolveBinarySync()
60
- if (!binary) return {}
61
-
62
- const args = ['keypair']
63
- if (options.noSpinner) args.push('--no-spinner')
64
- if (options.token) args.push('--token', options.token)
65
- if (options.envFilepath) args.push('-f', options.envFilepath)
66
- if (options.command) args.push('--metadata', this._serializeMetadata(options))
67
- if (publicKey) args.push(publicKey)
68
-
69
- try {
70
- return JSON.parse(this._execInteractiveSync(binary, args))
71
- } catch (_e) {
72
- return {}
73
- }
74
- }
75
-
76
- observe (payload) {
77
- if (this._isForcedOff()) return
78
-
79
- const binary = this._resolveBinarySync()
80
- if (!binary) return
81
-
82
- let status = 'off'
83
- try {
84
- status = this._execSync(binary, ['status'])
85
- } catch (_e) {
86
- return
87
- }
88
- if (status === 'off') return
89
-
90
- const encoded = Buffer.from(JSON.stringify(payload)).toString('base64')
91
- try {
92
- const subprocess = childProcess.spawn(binary, ['observe', encoded], {
93
- stdio: 'ignore',
94
- detached: true
95
- })
96
- subprocess.unref()
97
- } catch (_e) {
98
- // noop
99
- }
100
- }
101
-
102
- _serializeMetadata (options) {
103
- return JSON.stringify({
104
- command: this._serializeCommand(options.command)
105
- })
106
- }
107
-
108
- _serializeCommand (command) {
109
- if (Array.isArray(command)) return command.map((arg) => `${arg}`).join(' ')
110
- return `${command}`
111
- }
112
-
113
- async _exec (binary, args) {
114
- const { stdout, stderr } = await execFile(binary, args)
115
- if (stderr && stderr.length > 0) {
116
- process.stderr.write(stderr.toString())
117
- }
118
- return stdout.toString().trim()
119
- }
120
-
121
- _execSync (binary, args) {
122
- logger.debug(binary)
123
- logger.debug(args)
124
- return childProcess.execFileSync(binary, args).toString().trim()
125
- }
126
-
127
- _execInteractive (binary, args) {
128
- return new Promise((resolve, reject) => {
129
- const spawnOptions = {
130
- stdio: ['inherit', 'pipe', 'inherit']
131
- }
132
- const subprocess = childProcess.spawn(binary, args, spawnOptions)
133
- let stdout = ''
134
-
135
- subprocess.stdout.on('data', (data) => {
136
- stdout += data.toString()
137
- })
138
- subprocess.on('error', reject)
139
- subprocess.on('close', (code) => {
140
- if (code !== 0) {
141
- reject(new Error(`${binary} ${args.join(' ')} exited with code ${code}`))
142
- return
143
- }
144
-
145
- resolve(stdout.trim())
146
- })
147
- })
148
- }
149
-
150
- _execInteractiveSync (binary, args) {
151
- logger.debug(binary)
152
- logger.debug(args)
153
- return childProcess.execFileSync(binary, args, {
154
- stdio: ['inherit', 'pipe', 'inherit']
155
- }).toString().trim()
156
- }
157
-
158
- async _resolveBinary () {
159
- if (this._binaryPromise) return this._binaryPromise
160
-
161
- this._binaryPromise = (async () => {
162
- for (const binary of this._binaryCandidates()) {
163
- try {
164
- await this._exec(binary, ['--version'])
165
- return binary
166
- } catch (_e) {}
167
- }
168
-
169
- return null
170
- })()
171
-
172
- return this._binaryPromise
173
- }
174
-
175
- _resolveBinarySync () {
176
- if (this._binarySync !== undefined) return this._binarySync
177
-
178
- for (const binary of this._binaryCandidates()) {
179
- try {
180
- this._execSync(binary, ['--version'])
181
- this._binarySync = binary
182
- return this._binarySync
183
- } catch (err) {
184
- logger.debug(err.message)
185
- }
186
- }
187
-
188
- this._binarySync = null
189
- return null
190
- }
191
-
192
- _binaryCandidates () {
193
- return BINARY_NAMES.flatMap((binary) => [
194
- path.resolve(process.cwd(), `node_modules/.bin/${binary}`),
195
- binary
196
- ])
197
- }
198
-
199
- _isForcedOff () {
200
- return process.env.DOTENVX_NO_ARMOR === 'true' || process.env.DOTENVX_NO_VLT === 'true' || process.env.DOTENVX_NO_OPS === 'true'
201
- }
202
- }
203
-
204
- module.exports = Armor