electerm 3.2.0 → 3.3.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/LICENSE.electron.txt +21 -0
- package/npm/install.js +99 -32
- package/npm/utils.js +180 -0
- package/package.json +2 -3
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright (c) Electron contributors
|
|
2
|
+
Copyright (c) 2013-2020 GitHub Inc.
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
a copy of this software and associated documentation files (the
|
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/npm/install.js
CHANGED
|
@@ -5,14 +5,50 @@
|
|
|
5
5
|
const os = require('os')
|
|
6
6
|
const { resolve } = require('path')
|
|
7
7
|
const { exec, rm, mv } = require('shelljs')
|
|
8
|
-
const
|
|
9
|
-
const download = require('
|
|
8
|
+
const { execFile } = require('child_process')
|
|
9
|
+
const { phin, download } = require('./utils')
|
|
10
|
+
|
|
10
11
|
const plat = os.platform()
|
|
11
12
|
const arch = os.arch()
|
|
12
13
|
const { homepage } = require('../package.json')
|
|
14
|
+
|
|
13
15
|
const releaseInfoUrl = `${homepage}/data/electerm-github-release.json?_=${+new Date()}`
|
|
14
16
|
const versionUrl = `${homepage}/version.html?_=${+new Date()}`
|
|
15
17
|
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Security helpers
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate that a version string is a plain semver (e.g. "1.2.3" or "v1.2.3").
|
|
24
|
+
*/
|
|
25
|
+
function sanitizeVersion (ver) {
|
|
26
|
+
const clean = String(ver).trim().replace(/^v/, '')
|
|
27
|
+
if (!/^\d+\.\d+\.\d+$/.test(clean)) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Refusing to continue: remote version string failed validation: "${ver}"`
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
return clean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate that a release asset filename contains only safe characters.
|
|
37
|
+
*/
|
|
38
|
+
function sanitizeFilename (name) {
|
|
39
|
+
const clean = String(name).trim()
|
|
40
|
+
if (!/^[\w.-]+$/.test(clean)) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Refusing to continue: remote filename failed validation: "${name}"`
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
return clean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Core helpers
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
16
52
|
function down (url, extract = true) {
|
|
17
53
|
const local = resolve(__dirname, '../')
|
|
18
54
|
console.log('downloading ' + url)
|
|
@@ -22,7 +58,7 @@ function down (url, extract = true) {
|
|
|
22
58
|
}
|
|
23
59
|
|
|
24
60
|
function getVer () {
|
|
25
|
-
return
|
|
61
|
+
return phin({
|
|
26
62
|
url: versionUrl,
|
|
27
63
|
timeout: 15000
|
|
28
64
|
})
|
|
@@ -30,7 +66,7 @@ function getVer () {
|
|
|
30
66
|
}
|
|
31
67
|
|
|
32
68
|
function getReleaseInfo (filter) {
|
|
33
|
-
return
|
|
69
|
+
return phin({
|
|
34
70
|
url: releaseInfoUrl,
|
|
35
71
|
timeout: 15000
|
|
36
72
|
})
|
|
@@ -52,6 +88,10 @@ function showFinalMessage () {
|
|
|
52
88
|
console.log('========================================\n')
|
|
53
89
|
}
|
|
54
90
|
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Platform detection helpers
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
55
95
|
// Check if running on Windows 7 or earlier
|
|
56
96
|
function isWindows7OrEarlier (platform, release) {
|
|
57
97
|
if (platform !== 'win32') return false
|
|
@@ -120,20 +160,30 @@ function getDownloadPattern (platform, architecture, options = {}) {
|
|
|
120
160
|
return { pattern: new RegExp(`linux-x64${suffix}\\.tar\\.gz$`), type: `linux-x64${suffix}` }
|
|
121
161
|
}
|
|
122
162
|
}
|
|
163
|
+
|
|
123
164
|
return { pattern: null, type: 'unsupported' }
|
|
124
165
|
}
|
|
125
166
|
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Platform installers
|
|
126
169
|
async function runLinux (folderName, filePattern) {
|
|
127
|
-
const
|
|
128
|
-
const
|
|
170
|
+
const rawVer = await getVer()
|
|
171
|
+
const ver = sanitizeVersion(rawVer) // throws if tampered
|
|
172
|
+
|
|
173
|
+
const target = resolve(__dirname, `../electerm-${ver}-${folderName}`)
|
|
129
174
|
const targetNew = resolve(__dirname, '../electerm')
|
|
130
|
-
|
|
175
|
+
|
|
176
|
+
// Use shelljs array API — no shell string interpolation
|
|
177
|
+
rm('-rf', [target, targetNew])
|
|
178
|
+
|
|
131
179
|
const releaseInfo = await getReleaseInfo(r => r.name.includes(filePattern))
|
|
132
180
|
if (!releaseInfo) {
|
|
133
181
|
throw new Error(`No release found for pattern: ${filePattern}`)
|
|
134
182
|
}
|
|
183
|
+
|
|
135
184
|
await down(releaseInfo.browser_download_url)
|
|
136
|
-
|
|
185
|
+
|
|
186
|
+
mv(target, targetNew)
|
|
137
187
|
showFinalMessage()
|
|
138
188
|
exec('electerm')
|
|
139
189
|
}
|
|
@@ -144,37 +194,48 @@ async function runMac (archName) {
|
|
|
144
194
|
if (!releaseInfo) {
|
|
145
195
|
throw new Error(`No release found for Mac ${archName}`)
|
|
146
196
|
}
|
|
197
|
+
|
|
198
|
+
const safeName = sanitizeFilename(releaseInfo.name) // throws if tampered
|
|
147
199
|
await down(releaseInfo.browser_download_url, false)
|
|
148
|
-
|
|
200
|
+
|
|
201
|
+
const target = resolve(__dirname, '../', safeName)
|
|
149
202
|
showFinalMessage()
|
|
150
|
-
|
|
203
|
+
|
|
204
|
+
// execFile does not spawn a shell — no injection possible
|
|
205
|
+
execFile('open', [target])
|
|
151
206
|
}
|
|
152
207
|
|
|
153
|
-
// macOS 10.x specific version
|
|
154
208
|
async function runMac10 () {
|
|
155
209
|
const releaseInfo = await getReleaseInfo(r => /mac10-x64\.dmg$/.test(r.name))
|
|
156
210
|
if (!releaseInfo) {
|
|
157
211
|
throw new Error('No release found for macOS 10.x')
|
|
158
212
|
}
|
|
213
|
+
|
|
214
|
+
const safeName = sanitizeFilename(releaseInfo.name) // throws if tampered
|
|
159
215
|
await down(releaseInfo.browser_download_url, false)
|
|
160
|
-
|
|
216
|
+
|
|
217
|
+
const target = resolve(__dirname, '../', safeName)
|
|
161
218
|
showFinalMessage()
|
|
162
|
-
|
|
219
|
+
|
|
220
|
+
// execFile does not spawn a shell — no injection possible
|
|
221
|
+
execFile('open', [target])
|
|
163
222
|
}
|
|
164
223
|
|
|
165
224
|
async function runWin (archName) {
|
|
166
|
-
const
|
|
167
|
-
const
|
|
225
|
+
const rawVer = await getVer()
|
|
226
|
+
const ver = sanitizeVersion(rawVer) // consistent hardening
|
|
227
|
+
|
|
228
|
+
const target = resolve(__dirname, `../electerm-${ver}-win-${archName}`)
|
|
168
229
|
const targetNew = resolve(__dirname, '../electerm')
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
])
|
|
230
|
+
|
|
231
|
+
rm('-rf', [target, targetNew])
|
|
232
|
+
|
|
173
233
|
const pattern = new RegExp(`electerm-\\d+\\.\\d+\\.\\d+-win-${archName}\\.tar\\.gz$`)
|
|
174
234
|
const releaseInfo = await getReleaseInfo(r => pattern.test(r.name))
|
|
175
235
|
if (!releaseInfo) {
|
|
176
236
|
throw new Error(`No release found for Windows ${archName}`)
|
|
177
237
|
}
|
|
238
|
+
|
|
178
239
|
await down(releaseInfo.browser_download_url)
|
|
179
240
|
await mv(target, targetNew)
|
|
180
241
|
showFinalMessage()
|
|
@@ -183,23 +244,29 @@ async function runWin (archName) {
|
|
|
183
244
|
|
|
184
245
|
// Windows 7 specific version
|
|
185
246
|
async function runWin7 () {
|
|
186
|
-
const
|
|
187
|
-
const
|
|
247
|
+
const rawVer = await getVer()
|
|
248
|
+
const ver = sanitizeVersion(rawVer) // consistent hardening
|
|
249
|
+
|
|
250
|
+
const target = resolve(__dirname, `../electerm-${ver}-win7`)
|
|
188
251
|
const targetNew = resolve(__dirname, '../electerm')
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
])
|
|
252
|
+
|
|
253
|
+
rm('-rf', [target, targetNew])
|
|
254
|
+
|
|
193
255
|
const releaseInfo = await getReleaseInfo(r => /electerm-\d+\.\d+\.\d+-win7\.tar\.gz$/.test(r.name))
|
|
194
256
|
if (!releaseInfo) {
|
|
195
257
|
throw new Error('No release found for Windows 7')
|
|
196
258
|
}
|
|
259
|
+
|
|
197
260
|
await down(releaseInfo.browser_download_url)
|
|
198
261
|
await mv(target, targetNew)
|
|
199
262
|
showFinalMessage()
|
|
200
263
|
require('child_process').execFile(`${targetNew}\\electerm.exe`)
|
|
201
264
|
}
|
|
202
265
|
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// Main
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
|
|
203
270
|
async function main () {
|
|
204
271
|
console.log(`Detected platform: ${plat}, architecture: ${arch}`)
|
|
205
272
|
|
|
@@ -216,34 +283,28 @@ async function main () {
|
|
|
216
283
|
|
|
217
284
|
try {
|
|
218
285
|
if (plat === 'win32') {
|
|
219
|
-
// Windows: x64, arm64, win7
|
|
220
286
|
if (win7) {
|
|
221
287
|
await runWin7()
|
|
222
288
|
} else if (arch === 'arm64') {
|
|
223
289
|
await runWin('arm64')
|
|
224
290
|
} else {
|
|
225
|
-
// Default to x64 for all other Windows architectures
|
|
226
291
|
await runWin('x64')
|
|
227
292
|
}
|
|
228
293
|
} else if (plat === 'darwin') {
|
|
229
|
-
// macOS: x64, arm64, mac10
|
|
230
294
|
if (mac10) {
|
|
231
295
|
await runMac10()
|
|
232
296
|
} else if (arch === 'arm64') {
|
|
233
297
|
await runMac('arm64')
|
|
234
298
|
} else {
|
|
235
|
-
// Default to x64 for Intel Macs
|
|
236
299
|
await runMac('x64')
|
|
237
300
|
}
|
|
238
301
|
} else if (plat === 'linux') {
|
|
239
|
-
// Linux: x64, arm64, armv7l (with legacy variants)
|
|
240
302
|
const suffix = linuxLegacy ? '-legacy' : ''
|
|
241
303
|
if (arch === 'arm64') {
|
|
242
304
|
await runLinux(`linux-arm64${suffix}`, `linux-arm64${suffix}.tar.gz`)
|
|
243
305
|
} else if (arch === 'arm') {
|
|
244
306
|
await runLinux(`linux-armv7l${suffix}`, `linux-armv7l${suffix}.tar.gz`)
|
|
245
307
|
} else {
|
|
246
|
-
// Default to x64 for all other Linux architectures
|
|
247
308
|
await runLinux(`linux-x64${suffix}`, `linux-x64${suffix}.tar.gz`)
|
|
248
309
|
}
|
|
249
310
|
} else {
|
|
@@ -261,12 +322,18 @@ async function main () {
|
|
|
261
322
|
}
|
|
262
323
|
}
|
|
263
324
|
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// Exports
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
|
|
264
329
|
// Export functions for testing
|
|
265
330
|
module.exports = {
|
|
266
331
|
isWindows7OrEarlier,
|
|
267
332
|
isMacOS10,
|
|
268
333
|
isLinuxLegacy,
|
|
269
|
-
getDownloadPattern
|
|
334
|
+
getDownloadPattern,
|
|
335
|
+
sanitizeVersion,
|
|
336
|
+
sanitizeFilename
|
|
270
337
|
}
|
|
271
338
|
|
|
272
339
|
// Run main only if this file is executed directly
|
package/npm/utils.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for npm installer
|
|
3
|
+
* Replaces download and phin packages with native Node.js http/https and tar
|
|
4
|
+
* Supports Node.js 16+
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const https = require('https')
|
|
8
|
+
const http = require('http')
|
|
9
|
+
const fs = require('fs')
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const tar = require('tar')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Make an HTTP GET request
|
|
15
|
+
* @param {string} url - URL to fetch
|
|
16
|
+
* @param {number} timeout - Request timeout in milliseconds (default: 15000)
|
|
17
|
+
* @returns {Promise<string>} Response body as string
|
|
18
|
+
*/
|
|
19
|
+
function httpGet (url, timeout = 15000) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const client = url.startsWith('https') ? https : http
|
|
22
|
+
|
|
23
|
+
const req = client.get(url, { timeout }, (res) => {
|
|
24
|
+
// Handle redirects
|
|
25
|
+
if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) {
|
|
26
|
+
if (res.headers.location) {
|
|
27
|
+
resolve(httpGet(res.headers.location, timeout))
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (res.statusCode !== 200) {
|
|
33
|
+
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage || 'Unknown error'}`))
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const chunks = []
|
|
38
|
+
res.on('data', (chunk) => chunks.push(chunk))
|
|
39
|
+
res.on('end', () => {
|
|
40
|
+
const buffer = Buffer.concat(chunks)
|
|
41
|
+
resolve(buffer.toString())
|
|
42
|
+
})
|
|
43
|
+
res.on('error', reject)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
req.on('error', reject)
|
|
47
|
+
req.on('timeout', () => {
|
|
48
|
+
req.destroy()
|
|
49
|
+
reject(new Error(`Request timeout after ${timeout}ms`))
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Download a file from URL to local path
|
|
56
|
+
* @param {string} url - URL to download from
|
|
57
|
+
* @param {string} dest - Destination directory path
|
|
58
|
+
* @returns {Promise<string>} Path to downloaded file
|
|
59
|
+
*/
|
|
60
|
+
function downloadFile (url, dest) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const client = url.startsWith('https') ? https : http
|
|
63
|
+
|
|
64
|
+
const req = client.get(url, { timeout: 300000 }, (res) => {
|
|
65
|
+
// Handle redirects
|
|
66
|
+
if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) {
|
|
67
|
+
if (res.headers.location) {
|
|
68
|
+
resolve(downloadFile(res.headers.location, dest))
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (res.statusCode !== 200) {
|
|
74
|
+
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage || 'Unknown error'}`))
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Extract filename from URL or Content-Disposition header
|
|
79
|
+
let filename = 'download'
|
|
80
|
+
const contentDisposition = res.headers['content-disposition']
|
|
81
|
+
if (contentDisposition) {
|
|
82
|
+
const match = contentDisposition.match(/filename[^;=\n]*=(['"]?)([^'";\n]*)\1/)
|
|
83
|
+
if (match) {
|
|
84
|
+
filename = match[2]
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
const urlParts = url.split('/')
|
|
88
|
+
filename = urlParts[urlParts.length - 1].split('?')[0] || 'download'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const filepath = path.join(dest, filename)
|
|
92
|
+
const fileStream = fs.createWriteStream(filepath)
|
|
93
|
+
|
|
94
|
+
res.pipe(fileStream)
|
|
95
|
+
fileStream.on('finish', () => {
|
|
96
|
+
fileStream.close()
|
|
97
|
+
resolve(filepath)
|
|
98
|
+
})
|
|
99
|
+
fileStream.on('error', (err) => {
|
|
100
|
+
fs.unlink(filepath, () => {}) // Clean up partial download
|
|
101
|
+
reject(err)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
req.on('error', reject)
|
|
106
|
+
req.on('timeout', () => {
|
|
107
|
+
req.destroy()
|
|
108
|
+
reject(new Error('Download timeout after 300s'))
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract a tar.gz file to destination directory
|
|
115
|
+
* @param {string} filepath - Path to tar.gz file
|
|
116
|
+
* @param {string} dest - Destination directory
|
|
117
|
+
* @returns {Promise<void>}
|
|
118
|
+
*/
|
|
119
|
+
function extractTarGz (filepath, dest) {
|
|
120
|
+
return tar.extract({
|
|
121
|
+
file: filepath,
|
|
122
|
+
cwd: dest,
|
|
123
|
+
strip: 1 // Strip top-level directory
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Download and optionally extract a file
|
|
129
|
+
* Replaces the download package functionality
|
|
130
|
+
* @param {string} url - URL to download from
|
|
131
|
+
* @param {string} dest - Destination directory
|
|
132
|
+
* @param {boolean} extract - Whether to extract the file (default: true)
|
|
133
|
+
* @returns {Promise<void>}
|
|
134
|
+
*/
|
|
135
|
+
async function download (url, dest, { extract: doExtract = true } = {}) {
|
|
136
|
+
console.log('downloading ' + url)
|
|
137
|
+
|
|
138
|
+
const filepath = await downloadFile(url, dest)
|
|
139
|
+
|
|
140
|
+
if (doExtract && (filepath.endsWith('.tar.gz') || filepath.endsWith('.tgz'))) {
|
|
141
|
+
await extractTarGz(filepath, dest)
|
|
142
|
+
// Clean up the downloaded archive
|
|
143
|
+
try {
|
|
144
|
+
fs.unlinkSync(filepath)
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.warn('Warning: Failed to clean up downloaded archive:', err.message)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log('done!')
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Phin replacement - simple promisified HTTP client
|
|
155
|
+
* @param {object} options - Request options
|
|
156
|
+
* @param {string} options.url - URL to fetch
|
|
157
|
+
* @param {number} options.timeout - Request timeout (default: 15000)
|
|
158
|
+
* @returns {Promise<{body: string, statusCode: number, headers: object}>}
|
|
159
|
+
*/
|
|
160
|
+
async function phin (options) {
|
|
161
|
+
const { url, timeout = 15000 } = options
|
|
162
|
+
const body = await httpGet(url, timeout)
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
body: Buffer.from(body),
|
|
166
|
+
statusCode: 200,
|
|
167
|
+
headers: {}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Export promisified version
|
|
172
|
+
phin.promisified = phin
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
httpGet,
|
|
176
|
+
downloadFile,
|
|
177
|
+
extractTarGz,
|
|
178
|
+
download,
|
|
179
|
+
phin
|
|
180
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electerm",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Terminal/ssh/telnet/serialport/sftp client(linux, mac, win)",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"bin": "npm/electerm",
|
|
@@ -28,8 +28,7 @@
|
|
|
28
28
|
"preferGlobal": true,
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"shelljs": "*",
|
|
31
|
-
"
|
|
32
|
-
"download": "*"
|
|
31
|
+
"tar": "*"
|
|
33
32
|
},
|
|
34
33
|
"files": [
|
|
35
34
|
"npm",
|