@dotenvx/dotenvx-ops 0.15.6

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 (51) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +71 -0
  3. package/README.md +9 -0
  4. package/package.json +66 -0
  5. package/src/cli/actions/login.js +118 -0
  6. package/src/cli/actions/logout.js +42 -0
  7. package/src/cli/actions/observe.js +33 -0
  8. package/src/cli/actions/settings/device.js +24 -0
  9. package/src/cli/actions/settings/hostname.js +20 -0
  10. package/src/cli/actions/settings/token.js +24 -0
  11. package/src/cli/actions/settings/username.js +22 -0
  12. package/src/cli/actions/status.js +13 -0
  13. package/src/cli/actions/sync.js +39 -0
  14. package/src/cli/commands/settings.js +39 -0
  15. package/src/cli/dotenvx-ops.js +93 -0
  16. package/src/db/device.js +73 -0
  17. package/src/db/session.js +119 -0
  18. package/src/lib/api/postLogout.js +34 -0
  19. package/src/lib/api/postOauthDeviceCode.js +42 -0
  20. package/src/lib/api/postOauthToken.js +38 -0
  21. package/src/lib/api/postObserve.js +59 -0
  22. package/src/lib/api/postSync.js +65 -0
  23. package/src/lib/helpers/buildApiError.js +13 -0
  24. package/src/lib/helpers/buildOauthError.js +13 -0
  25. package/src/lib/helpers/clipboardy/fallbacks/linux/xsel +0 -0
  26. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_i686.exe +0 -0
  27. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_x86_64.exe +0 -0
  28. package/src/lib/helpers/clipboardy/linux.js +57 -0
  29. package/src/lib/helpers/clipboardy/macos.js +14 -0
  30. package/src/lib/helpers/clipboardy/termux.js +41 -0
  31. package/src/lib/helpers/clipboardy/windows.js +16 -0
  32. package/src/lib/helpers/clipboardy.js +51 -0
  33. package/src/lib/helpers/confirm.js +17 -0
  34. package/src/lib/helpers/createSpinner.js +101 -0
  35. package/src/lib/helpers/decryptValue.js +10 -0
  36. package/src/lib/helpers/encryptValue.js +9 -0
  37. package/src/lib/helpers/errors.js +30 -0
  38. package/src/lib/helpers/formatCode.js +11 -0
  39. package/src/lib/helpers/gitBranch.js +13 -0
  40. package/src/lib/helpers/gitUrl.js +13 -0
  41. package/src/lib/helpers/http.js +17 -0
  42. package/src/lib/helpers/jsonToEnv.js +7 -0
  43. package/src/lib/helpers/mask.js +12 -0
  44. package/src/lib/helpers/packageJson.js +3 -0
  45. package/src/lib/helpers/smartMask.js +11 -0
  46. package/src/lib/helpers/truncate.js +10 -0
  47. package/src/lib/main.d.ts +129 -0
  48. package/src/lib/main.js +47 -0
  49. package/src/lib/services/logout.js +28 -0
  50. package/src/lib/services/status.js +11 -0
  51. package/src/lib/services/sync.js +94 -0
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+
3
+ /* c8 ignore start */
4
+ const { Command } = require('commander')
5
+ const program = new Command()
6
+
7
+ const { setLogLevel } = require('@dotenvx/dotenvx')
8
+
9
+ const packageJson = require('./../lib/helpers/packageJson')
10
+ const Session = require('./../db/session')
11
+ const sesh = new Session()
12
+
13
+ // global log levels
14
+ program
15
+ .usage('login')
16
+ .option('-l, --log-level <level>', 'set log level', 'info')
17
+ .option('-q, --quiet', 'sets log level to error')
18
+ .option('-v, --verbose', 'sets log level to verbose')
19
+ .option('-d, --debug', 'sets log level to debug')
20
+ .hook('preAction', (thisCommand, actionCommand) => {
21
+ const options = thisCommand.opts()
22
+
23
+ setLogLevel(options)
24
+ })
25
+
26
+ // cli
27
+ program
28
+ .name('dotenvx-ops')
29
+ .description(packageJson.description)
30
+ .version(packageJson.version)
31
+ .allowUnknownOption()
32
+
33
+ // dotenvx-ops observe base64String
34
+ const observeAction = require('./actions/observe')
35
+ program.command('observe')
36
+ .usage('<BASE64> [options]')
37
+ .description('[INTERNAL] observe')
38
+ .allowUnknownOption()
39
+ .argument('BASE64', 'BASE64')
40
+ .option('--hostname <url>', 'set hostname', sesh.hostname())
41
+ .option('--token <token>', 'set token')
42
+ .action(function (...args) {
43
+ observeAction.apply(this, args)
44
+ })
45
+
46
+ // dotenvx-ops sync
47
+ const syncAction = require('./actions/sync')
48
+ program
49
+ .command('sync')
50
+ .description('sync .env file(s)')
51
+ .option('-h, --hostname <url>', 'set hostname', sesh.hostname())
52
+ .action(syncAction)
53
+
54
+ // dotenvx-ops login
55
+ const loginAction = require('./actions/login')
56
+ program
57
+ .command('login')
58
+ .description('log in')
59
+ .option('--hostname <url>', 'set hostname', sesh.hostname())
60
+ .action(loginAction)
61
+
62
+ // dotenvx-ops logout
63
+ const logoutAction = require('./actions/logout')
64
+ program
65
+ .command('logout')
66
+ .description('log out')
67
+ .option('--hostname <url>', 'set hostname', sesh.hostname())
68
+ .action(logoutAction)
69
+
70
+ // dotenvx-ops status
71
+ const statusAction = require('./actions/status')
72
+ program
73
+ .command('status')
74
+ .description('status')
75
+ .action(statusAction)
76
+
77
+ // dotenvx-ops settings
78
+ program.addCommand(require('./commands/settings'))
79
+
80
+ // monkey-patch help output
81
+ program.helpInformation = function () {
82
+ const originalHelp = Command.prototype.helpInformation.call(this)
83
+ const lines = originalHelp.split('\n')
84
+
85
+ const filteredLines = lines.filter(line =>
86
+ !line.includes('INTERNAL') // hide observe command
87
+ )
88
+
89
+ return filteredLines.join('\n')
90
+ }
91
+
92
+ /* c8 ignore stop */
93
+ program.parse(process.argv)
@@ -0,0 +1,73 @@
1
+ const Conf = require('conf')
2
+ const { PrivateKey } = require('eciesjs')
3
+
4
+ const encryptValue = require('./../lib/helpers/encryptValue')
5
+ const decryptValue = require('./../lib/helpers/decryptValue')
6
+
7
+ class Device {
8
+ constructor () {
9
+ this.store = new Conf({
10
+ cwd: process.env.DOTENVX_CONFIG || undefined,
11
+ projectName: 'dotenvx',
12
+ configName: '.device-key',
13
+ projectSuffix: '',
14
+ fileExtension: '',
15
+ encryptionKey: 'dotenvxpro dotenvxpro dotenvxpro' // backwards compatible
16
+ })
17
+ }
18
+
19
+ touch () {
20
+ const _privateKey = this.privateKey()
21
+ const _publicKey = this.publicKey()
22
+
23
+ return {
24
+ privateKey: _privateKey,
25
+ publicKey: _publicKey
26
+ }
27
+ }
28
+
29
+ configPath () {
30
+ return this.store.path
31
+ }
32
+
33
+ privateKey () {
34
+ const currentPrivateKey = this.store.get('private_key/1')
35
+ if (currentPrivateKey && currentPrivateKey.length > 0) {
36
+ this.store.set('private_key/1', currentPrivateKey)
37
+
38
+ return currentPrivateKey
39
+ }
40
+
41
+ // generate privateKey for the first time
42
+ const kp = new PrivateKey()
43
+ const _privateKey = kp.secret.toString('hex')
44
+
45
+ this.store.set('private_key/1', _privateKey)
46
+
47
+ return _privateKey
48
+ }
49
+
50
+ publicKey () {
51
+ // must have private key to try and get public key
52
+ const privateKeyHex = this.privateKey()
53
+ if (!privateKeyHex || privateKeyHex.length < 1) {
54
+ return ''
55
+ }
56
+
57
+ // create keyPair object from hex string
58
+ const _privateKey = new PrivateKey(Buffer.from(privateKeyHex, 'hex'))
59
+
60
+ // compute publicKey from privateKey
61
+ return _privateKey.publicKey.toHex()
62
+ }
63
+
64
+ encrypt (value) {
65
+ return encryptValue(value, this.publicKey())
66
+ }
67
+
68
+ decrypt (value) {
69
+ return decryptValue(value, this.privateKey())
70
+ }
71
+ }
72
+
73
+ module.exports = Device
@@ -0,0 +1,119 @@
1
+ const Conf = require('conf')
2
+ const dotenv = require('dotenv')
3
+ const si = require('systeminformation')
4
+
5
+ const Device = require('./device')
6
+ const jsonToEnv = require('./../lib/helpers/jsonToEnv')
7
+
8
+ class Session {
9
+ constructor () {
10
+ this.store = new Conf({
11
+ cwd: process.env.DOTENVX_CONFIG || undefined,
12
+ projectName: 'dotenvx',
13
+ configName: '.env',
14
+ projectSuffix: '',
15
+ fileExtension: '',
16
+ serialize: function (json) {
17
+ return jsonToEnv(json)
18
+ },
19
+ // Convert .env format to an object
20
+ deserialize: function (env) {
21
+ return dotenv.parse(env)
22
+ }
23
+ })
24
+ }
25
+
26
+ status () {
27
+ // if logged in
28
+ if (this.username() && this.token()) {
29
+ return 'on'
30
+ }
31
+
32
+ return 'off'
33
+ }
34
+
35
+ //
36
+ // Get
37
+ //
38
+ hostname () {
39
+ return this.store.get('DOTENVX_OPS_HOSTNAME') || this.store.get('DOTENVX_RADAR_HOSTNAME') || 'https://ops.dotenvx.com'
40
+ }
41
+
42
+ username () {
43
+ return this.store.get('DOTENVX_OPS_USERNAME') || this.store.get('DOTENVX_RADAR_USERNAME') || undefined
44
+ }
45
+
46
+ token () {
47
+ return this.store.get('DOTENVX_OPS_TOKEN') || this.store.get('DOTENVX_RADAR_TOKEN') || undefined
48
+ }
49
+
50
+ devicePublicKey () {
51
+ return new Device().publicKey()
52
+ }
53
+
54
+ async systemInformation () {
55
+ const system = await si.system()
56
+ const osInfo = await si.osInfo()
57
+
58
+ return {
59
+ system_uuid: system.uuid,
60
+ os_platform: osInfo.platform,
61
+ os_arch: osInfo.arch
62
+ }
63
+ }
64
+
65
+ //
66
+ // Set/Delete
67
+ //
68
+ login (hostname, id, username, accessToken) {
69
+ if (!hostname) {
70
+ throw new Error('DOTENVX_OPS_HOSTNAME not set. Run [dotenvx-ops login]')
71
+ }
72
+
73
+ if (!id) {
74
+ throw new Error('DOTENVX_OPS_USER not set. Run [dotenvx-ops login]')
75
+ }
76
+
77
+ if (!username) {
78
+ throw new Error('DOTENVX_OPS_USERNAME not set. Run [dotenvx-ops login]')
79
+ }
80
+
81
+ if (!accessToken) {
82
+ throw new Error('DOTENVX_OPS_TOKEN not set. Run [dotenvx-ops login]')
83
+ }
84
+
85
+ this.store.set('DOTENVX_OPS_USER', id)
86
+ this.store.set('DOTENVX_OPS_USERNAME', username)
87
+ this.store.set('DOTENVX_OPS_TOKEN', accessToken)
88
+ this.store.set('DOTENVX_OPS_HOSTNAME', hostname)
89
+
90
+ return accessToken
91
+ }
92
+
93
+ logout (hostname, id, accessToken) {
94
+ if (!hostname) {
95
+ throw new Error('DOTENVX_OPS_HOSTNAME not set. Run [dotenvx-ops login]')
96
+ }
97
+
98
+ if (!id) {
99
+ throw new Error('DOTENVX_OPS_USER not set. Run [dotenvx-ops login]')
100
+ }
101
+
102
+ if (!accessToken) {
103
+ throw new Error('DOTENVX_OPS_TOKEN not set. Run [dotenvx-ops login]')
104
+ }
105
+
106
+ this.store.delete('DOTENVX_OPS_USER')
107
+ this.store.delete('DOTENVX_OPS_USERNAME')
108
+ this.store.delete('DOTENVX_OPS_TOKEN')
109
+ this.store.delete('DOTENVX_OPS_HOSTNAME')
110
+ this.store.delete('DOTENVX_RADAR_USER')
111
+ this.store.delete('DOTENVX_RADAR_USERNAME')
112
+ this.store.delete('DOTENVX_RADAR_TOKEN')
113
+ this.store.delete('DOTENVX_RADAR_HOSTNAME')
114
+
115
+ return true
116
+ }
117
+ }
118
+
119
+ module.exports = Session
@@ -0,0 +1,34 @@
1
+ const { http } = require('../../lib/helpers/http')
2
+
3
+ const buildApiError = require('../../lib/helpers/buildApiError')
4
+
5
+ class PostLogout {
6
+ constructor (hostname, token) {
7
+ this.hostname = hostname
8
+ this.token = token
9
+ }
10
+
11
+ async run () {
12
+ const token = this.token
13
+ const url = `${this.hostname}/api/logout`
14
+
15
+ const resp = await http(url, {
16
+ method: 'POST',
17
+ headers: {
18
+ Authorization: `Bearer ${token}`,
19
+ 'Content-Type': 'application/json'
20
+ },
21
+ body: JSON.stringify({})
22
+ })
23
+
24
+ const json = await resp.body.json()
25
+
26
+ if (resp.statusCode >= 400) {
27
+ throw buildApiError(resp.statusCode, json)
28
+ }
29
+
30
+ return json
31
+ }
32
+ }
33
+
34
+ module.exports = PostLogout
@@ -0,0 +1,42 @@
1
+ const { http } = require('../../lib/helpers/http')
2
+
3
+ // const Device = require('../../db/device')
4
+ const buildOauthError = require('../../lib/helpers/buildOauthError')
5
+
6
+ const OAUTH_CLIENT_ID = 'oac_dotenvxcli'
7
+
8
+ class PostOauthDeviceCode {
9
+ constructor (hostname, devicePublicKey, systemInformation) {
10
+ this.hostname = hostname
11
+ this.devicePublicKey = devicePublicKey
12
+ this.systemInformation = systemInformation
13
+ }
14
+
15
+ async run () {
16
+ const url = `${this.hostname}/oauth/device/code`
17
+ const devicePublicKey = this.devicePublicKey
18
+ const systemInformation = this.systemInformation
19
+
20
+ const resp = await http(url, {
21
+ method: 'POST',
22
+ headers: {
23
+ 'Content-Type': 'application/json'
24
+ },
25
+ body: JSON.stringify({
26
+ client_id: OAUTH_CLIENT_ID,
27
+ device_public_key: devicePublicKey,
28
+ system_information: systemInformation
29
+ })
30
+ })
31
+
32
+ const json = await resp.body.json()
33
+
34
+ if (resp.statusCode >= 400) {
35
+ throw buildOauthError(resp.statusCode, json)
36
+ }
37
+
38
+ return json
39
+ }
40
+ }
41
+
42
+ module.exports = PostOauthDeviceCode
@@ -0,0 +1,38 @@
1
+ const { http } = require('../../lib/helpers/http')
2
+
3
+ const buildOauthError = require('../../lib/helpers/buildOauthError')
4
+
5
+ const OAUTH_CLIENT_ID = 'oac_dotenvxcli'
6
+
7
+ class PostOauthToken {
8
+ constructor (hostname, deviceCode) {
9
+ this.hostname = hostname
10
+ this.deviceCode = deviceCode
11
+ }
12
+
13
+ async run () {
14
+ const url = `${this.hostname}/oauth/token`
15
+
16
+ const resp = await http(url, {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json'
20
+ },
21
+ body: JSON.stringify({
22
+ client_id: OAUTH_CLIENT_ID,
23
+ device_code: this.deviceCode,
24
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
25
+ })
26
+ })
27
+
28
+ const json = await resp.body.json()
29
+
30
+ if (resp.statusCode >= 400) {
31
+ throw buildOauthError(resp.statusCode, json)
32
+ }
33
+
34
+ return json
35
+ }
36
+ }
37
+
38
+ module.exports = PostOauthToken
@@ -0,0 +1,59 @@
1
+ const { http } = require('../../lib/helpers/http')
2
+ const buildApiError = require('../../lib/helpers/buildApiError')
3
+ const packageJson = require('../../lib/helpers/packageJson')
4
+
5
+ class PostObserve {
6
+ constructor (hostname, token, encoded, pwd = null, gitUrl = null, gitBranch = null, systemUuid = null, osPlatform = null, osArch = null) {
7
+ this.hostname = hostname || 'https://ops.dotenvx.com'
8
+ this.token = token
9
+ this.encoded = encoded
10
+ this.pwd = pwd
11
+ this.gitUrl = gitUrl
12
+ this.gitBranch = gitBranch
13
+ this.systemUuid = systemUuid
14
+ this.osPlatform = osPlatform
15
+ this.osArch = osArch
16
+ }
17
+
18
+ async run () {
19
+ const token = this.token
20
+ const url = `${this.hostname}/api/observe`
21
+ const encoded = this.encoded
22
+ const observedAt = new Date().toISOString()
23
+ const pwd = this.pwd
24
+ const gitUrl = this.gitUrl
25
+ const gitBranch = this.gitBranch
26
+ const systemUuid = this.systemUuid
27
+ const osPlatform = this.osPlatform
28
+ const osArch = this.osArch
29
+
30
+ const resp = await http(url, {
31
+ method: 'POST',
32
+ headers: {
33
+ Authorization: `Bearer ${token}`,
34
+ 'Content-Type': 'application/json'
35
+ },
36
+ body: JSON.stringify({
37
+ encoded,
38
+ observed_at: observedAt,
39
+ pwd,
40
+ git_url: gitUrl,
41
+ git_branch: gitBranch,
42
+ system_uuid: systemUuid,
43
+ os_platform: osPlatform,
44
+ os_arch: osArch,
45
+ cli_version: packageJson.version
46
+ })
47
+ })
48
+
49
+ const json = await resp.body.json()
50
+
51
+ if (resp.statusCode >= 400) {
52
+ throw buildApiError(resp.statusCode, json)
53
+ }
54
+
55
+ return json
56
+ }
57
+ }
58
+
59
+ module.exports = PostObserve
@@ -0,0 +1,65 @@
1
+ const { http } = require('../../lib/helpers/http')
2
+ const buildApiError = require('../../lib/helpers/buildApiError')
3
+ const packageJson = require('../../lib/helpers/packageJson')
4
+
5
+ class PostSync {
6
+ constructor (hostname, token, devicePublicKey, encoded, dotenvxProjectId, pwd = null, gitUrl = null, gitBranch = null, systemUuid = null, osPlatform = null, osArch = null) {
7
+ this.hostname = hostname || 'https://ops.dotenvx.com'
8
+ this.token = token
9
+ this.devicePublicKey = devicePublicKey
10
+ this.encoded = encoded
11
+ this.dotenvxProjectId = dotenvxProjectId
12
+ this.pwd = pwd
13
+ this.gitUrl = gitUrl
14
+ this.gitBranch = gitBranch
15
+ this.systemUuid = systemUuid
16
+ this.osPlatform = osPlatform
17
+ this.osArch = osArch
18
+ }
19
+
20
+ async run () {
21
+ const token = this.token
22
+ const devicePublicKey = this.devicePublicKey
23
+ const url = `${this.hostname}/api/sync`
24
+ const encoded = this.encoded
25
+ const dotenvxProjectId = this.dotenvxProjectId
26
+ const syncedAt = new Date().toISOString()
27
+ const pwd = this.pwd
28
+ const gitUrl = this.gitUrl
29
+ const gitBranch = this.gitBranch
30
+ const systemUuid = this.systemUuid
31
+ const osPlatform = this.osPlatform
32
+ const osArch = this.osArch
33
+
34
+ const resp = await http(url, {
35
+ method: 'POST',
36
+ headers: {
37
+ Authorization: `Bearer ${token}`,
38
+ 'Content-Type': 'application/json'
39
+ },
40
+ body: JSON.stringify({
41
+ device_public_key: devicePublicKey,
42
+ encoded,
43
+ dotenvx_project_id: dotenvxProjectId,
44
+ synced_at: syncedAt,
45
+ pwd,
46
+ git_url: gitUrl,
47
+ git_branch: gitBranch,
48
+ system_uuid: systemUuid,
49
+ os_platform: osPlatform,
50
+ os_arch: osArch,
51
+ cli_version: packageJson.version
52
+ })
53
+ })
54
+
55
+ const json = await resp.body.json()
56
+
57
+ if (resp.statusCode >= 400) {
58
+ throw buildApiError(resp.statusCode, json)
59
+ }
60
+
61
+ return json
62
+ }
63
+ }
64
+
65
+ module.exports = PostSync
@@ -0,0 +1,13 @@
1
+ function buildApiError (statusCode, json) {
2
+ const code = json.error.code || statusCode.toString()
3
+ const message = `[${code}] ${json.error.message}`
4
+ const help = `[${code}] ${json.error.help || JSON.stringify(json)}`
5
+
6
+ const error = new Error(message)
7
+ error.code = code
8
+ error.help = help
9
+
10
+ return error
11
+ }
12
+
13
+ module.exports = buildApiError
@@ -0,0 +1,13 @@
1
+ function buildOauthError (statusCode, json) {
2
+ const code = json.error // TAKE CAUTION CHANGING THIS. the polling for an oauth token relies on it
3
+ const message = `[${code}] ${json.error_description}`
4
+ const help = `[${code}] ${JSON.stringify(json)}`
5
+
6
+ const error = new Error(message)
7
+ error.code = code
8
+ error.help = help
9
+
10
+ return error
11
+ }
12
+
13
+ module.exports = buildOauthError
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+ const path = require('path')
3
+ const execa = require('execa')
4
+
5
+ const xsel = 'xsel'
6
+ const xselFallback = path.join(__dirname, './fallbacks/linux/xsel')
7
+
8
+ const copyArguments = ['--clipboard', '--input']
9
+ const pasteArguments = ['--clipboard', '--output']
10
+
11
+ const makeError = (xselError, fallbackError) => {
12
+ let error
13
+ if (xselError.code === 'ENOENT') {
14
+ error = new Error('Couldn\'t find the `xsel` binary and fallback didn\'t work. On Debian/Ubuntu you can install xsel with: sudo apt install xsel')
15
+ } else {
16
+ error = new Error('Both xsel and fallback failed')
17
+ error.xselError = xselError
18
+ }
19
+
20
+ error.fallbackError = fallbackError
21
+ return error
22
+ }
23
+
24
+ const xselWithFallback = async (argumentList, options) => {
25
+ try {
26
+ return await execa.stdout(xsel, argumentList, options)
27
+ } catch (xselError) {
28
+ try {
29
+ return await execa.stdout(xselFallback, argumentList, options)
30
+ } catch (fallbackError) {
31
+ throw makeError(xselError, fallbackError)
32
+ }
33
+ }
34
+ }
35
+
36
+ const xselWithFallbackSync = (argumentList, options) => {
37
+ try {
38
+ return execa.sync(xsel, argumentList, options)
39
+ } catch (xselError) {
40
+ try {
41
+ return execa.sync(xselFallback, argumentList, options)
42
+ } catch (fallbackError) {
43
+ throw makeError(xselError, fallbackError)
44
+ }
45
+ }
46
+ }
47
+
48
+ module.exports = {
49
+ copy: async options => {
50
+ await xselWithFallback(copyArguments, options)
51
+ },
52
+ copySync: options => {
53
+ xselWithFallbackSync(copyArguments, options)
54
+ },
55
+ paste: options => xselWithFallback(pasteArguments, options),
56
+ pasteSync: options => xselWithFallbackSync(pasteArguments, options)
57
+ }
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+ const execa = require('execa')
3
+
4
+ const env = {
5
+ ...process.env,
6
+ LC_CTYPE: 'UTF-8'
7
+ }
8
+
9
+ module.exports = {
10
+ copy: async options => execa('pbcopy', { ...options, env }),
11
+ paste: async options => execa.stdout('pbpaste', { ...options, env }),
12
+ copySync: options => execa.sync('pbcopy', { ...options, env }),
13
+ pasteSync: options => execa.sync('pbpaste', { ...options, env })
14
+ }
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+ const execa = require('execa')
3
+
4
+ const handler = error => {
5
+ if (error.code === 'ENOENT') {
6
+ throw new Error('Couldn\'t find the termux-api scripts. You can install them with: apt install termux-api')
7
+ }
8
+
9
+ throw error
10
+ }
11
+
12
+ module.exports = {
13
+ copy: async options => {
14
+ try {
15
+ await execa('termux-clipboard-set', options)
16
+ } catch (error) {
17
+ handler(error)
18
+ }
19
+ },
20
+ paste: async options => {
21
+ try {
22
+ return await execa.stdout('termux-clipboard-get', options)
23
+ } catch (error) {
24
+ handler(error)
25
+ }
26
+ },
27
+ copySync: options => {
28
+ try {
29
+ execa.sync('termux-clipboard-set', options)
30
+ } catch (error) {
31
+ handler(error)
32
+ }
33
+ },
34
+ pasteSync: options => {
35
+ try {
36
+ return execa.sync('termux-clipboard-get', options)
37
+ } catch (error) {
38
+ handler(error)
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+ const path = require('path')
3
+ const execa = require('execa')
4
+ const arch = require('arch')
5
+
6
+ // Binaries from: https://github.com/sindresorhus/clipboardy/tree/v2.3.0
7
+ const windowBinaryPath = arch() === 'x64'
8
+ ? path.join(__dirname, './fallbacks/windows/clipboard_x86_64.exe')
9
+ : path.join(__dirname, './fallbacks/windows/clipboard_i686.exe')
10
+
11
+ module.exports = {
12
+ copy: async options => execa(windowBinaryPath, ['--copy'], options),
13
+ paste: async options => execa.stdout(windowBinaryPath, ['--paste'], options),
14
+ copySync: options => execa.sync(windowBinaryPath, ['--copy'], options),
15
+ pasteSync: options => execa.sync(windowBinaryPath, ['--paste'], options)
16
+ }