electerm 3.3.2 → 3.3.5
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/README.md +0 -3
- package/npm/electerm +21 -1
- package/npm/install.js +16 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,9 +90,6 @@ Check [https://electerm-repos.html5beta.com/deb](https://electerm-repos.html5bet
|
|
|
90
90
|
```bash
|
|
91
91
|
npm i -g electerm
|
|
92
92
|
|
|
93
|
-
# After installation, it will immediately open for windows and linux,
|
|
94
|
-
# For macOS, it will open the drag to install panel
|
|
95
|
-
|
|
96
93
|
```
|
|
97
94
|
|
|
98
95
|
## Upgrade
|
package/npm/electerm
CHANGED
|
@@ -34,6 +34,21 @@ function getElectermExePath () {
|
|
|
34
34
|
return path.join(packageRoot, 'electerm', 'electerm')
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function isSandboxReady () {
|
|
38
|
+
// chrome-sandbox must be owned by root (uid 0) and have setuid bit set (mode 4755)
|
|
39
|
+
// This requires root during install, which npm global install does not provide.
|
|
40
|
+
try {
|
|
41
|
+
const sandboxPath = path.join(packageRoot, 'electerm', 'chrome-sandbox')
|
|
42
|
+
if (!fs.existsSync(sandboxPath)) return false
|
|
43
|
+
const stat = fs.statSync(sandboxPath)
|
|
44
|
+
const hasSetuid = (stat.mode & 0o4000) !== 0
|
|
45
|
+
const ownedByRoot = stat.uid === 0
|
|
46
|
+
return hasSetuid && ownedByRoot
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
function launchElecterm () {
|
|
38
53
|
const exePath = getElectermExePath()
|
|
39
54
|
|
|
@@ -46,7 +61,12 @@ function launchElecterm () {
|
|
|
46
61
|
process.exit(1)
|
|
47
62
|
}
|
|
48
63
|
|
|
49
|
-
const
|
|
64
|
+
const extraArgs = []
|
|
65
|
+
if (plat === 'linux' && !isSandboxReady()) {
|
|
66
|
+
extraArgs.push('--no-sandbox')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const child = spawn(exePath, [...extraArgs, ...process.argv.slice(2)], {
|
|
50
70
|
stdio: 'inherit',
|
|
51
71
|
detached: plat !== 'win32',
|
|
52
72
|
windowsHide: false
|
package/npm/install.js
CHANGED
|
@@ -192,11 +192,15 @@ async function runLinux (folderName, filePattern) {
|
|
|
192
192
|
mv(join(tmpDir, extractedFolder), extractDir)
|
|
193
193
|
|
|
194
194
|
// Fix chrome-sandbox permissions on Linux (Electron requires specific permissions)
|
|
195
|
+
// Note: setting the setuid bit requires root ownership, which npm install cannot provide.
|
|
196
|
+
// The launcher handles this by passing --no-sandbox when the sandbox is not root-owned.
|
|
195
197
|
if (plat === 'linux') {
|
|
196
198
|
const chromeSandboxPath = join(extractDir, 'chrome-sandbox')
|
|
197
199
|
if (fs.existsSync(chromeSandboxPath)) {
|
|
198
|
-
console.log('
|
|
199
|
-
|
|
200
|
+
console.log(' Note: To enable the Electron sandbox, run:')
|
|
201
|
+
console.log(` sudo chown root:root "${chromeSandboxPath}"`)
|
|
202
|
+
console.log(` sudo chmod 4755 "${chromeSandboxPath}"`)
|
|
203
|
+
console.log(' Otherwise, electerm will launch with --no-sandbox automatically.')
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
|
|
@@ -207,23 +211,16 @@ async function runLinux (folderName, filePattern) {
|
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
async function runWin (archName) {
|
|
210
|
-
console.log(' [DEBUG] runWin started')
|
|
211
|
-
console.log(` [DEBUG] packageRoot: ${packageRoot}`)
|
|
212
|
-
console.log(` [DEBUG] extractDir: ${extractDir}`)
|
|
213
|
-
|
|
214
214
|
const rawVer = await getVer()
|
|
215
215
|
const ver = sanitizeVersion(rawVer)
|
|
216
216
|
|
|
217
|
-
console.log(`
|
|
218
|
-
console.log(` Sanitized version: ${ver}`)
|
|
217
|
+
console.log(` Version: ${ver}`)
|
|
219
218
|
console.log(` Target: win-${archName}`)
|
|
220
219
|
|
|
221
220
|
const target = join(packageRoot, `electerm-${ver}-win-${archName}`)
|
|
222
|
-
console.log(` [DEBUG] Target folder: ${target}`)
|
|
223
221
|
|
|
224
|
-
console.log(' Cleaning old installations...')
|
|
225
222
|
rm('-rf', [target, extractDir])
|
|
226
|
-
|
|
223
|
+
fs.mkdirSync(extractDir, { recursive: true })
|
|
227
224
|
|
|
228
225
|
const pattern = new RegExp(`electerm-\\d+\\.\\d+\\.\\d+-win-${archName}\\.tar\\.gz$`)
|
|
229
226
|
console.log(' Fetching release info...')
|
|
@@ -231,70 +228,24 @@ async function runWin (archName) {
|
|
|
231
228
|
if (!releaseInfo) {
|
|
232
229
|
throw new Error(`No release found for Windows ${archName}`)
|
|
233
230
|
}
|
|
234
|
-
console.log(` [DEBUG] Release info found: ${JSON.stringify(releaseInfo, null, 2)}`)
|
|
235
231
|
|
|
232
|
+
// Download to a temp file, then extract directly to extractDir with strip:1
|
|
233
|
+
// (avoids a rename which can fail on Windows when AV has file locks)
|
|
236
234
|
const tmpDir = join(packageRoot, '.electerm-tmp')
|
|
237
|
-
console.log(` [DEBUG] Creating temp directory: ${tmpDir}`)
|
|
238
235
|
rm('-rf', tmpDir)
|
|
239
236
|
fs.mkdirSync(tmpDir, { recursive: true })
|
|
240
237
|
|
|
241
238
|
const proxyUrl = applyProxy(releaseInfo.browser_download_url)
|
|
242
|
-
console.log(`
|
|
243
|
-
console.log(` [DEBUG] Download URL: ${releaseInfo.browser_download_url}`)
|
|
239
|
+
console.log(` URL: ${proxyUrl}`)
|
|
244
240
|
|
|
245
|
-
console.log(' Downloading...')
|
|
246
241
|
const { filepath } = await download(releaseInfo.browser_download_url, tmpDir, { extract: false, displayName: releaseInfo.name })
|
|
247
|
-
console.log(` [DEBUG] Downloaded to: ${filepath}`)
|
|
248
|
-
console.log(` [DEBUG] File exists: ${fs.existsSync(filepath)}`)
|
|
249
|
-
console.log(` [DEBUG] File size: ${fs.statSync(filepath).size}`)
|
|
250
|
-
|
|
251
|
-
console.log(' Extracting...')
|
|
252
|
-
await extractTarGz(filepath, tmpDir)
|
|
253
|
-
console.log(' [DEBUG] Extraction complete')
|
|
254
|
-
|
|
255
|
-
console.log(' [DEBUG] Listing temp directory contents:')
|
|
256
|
-
const entries = fs.readdirSync(tmpDir)
|
|
257
|
-
entries.forEach(e => {
|
|
258
|
-
const fullPath = join(tmpDir, e)
|
|
259
|
-
const stat = fs.statSync(fullPath)
|
|
260
|
-
console.log(` [DEBUG] ${e} - ${stat.isDirectory() ? 'DIR' : 'FILE'} (${stat.size} bytes)`)
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
const extractedFolder = entries.find(e => fs.statSync(join(tmpDir, e)).isDirectory())
|
|
264
|
-
|
|
265
|
-
if (!extractedFolder) {
|
|
266
|
-
console.error(' [DEBUG] No directory found in extracted archive')
|
|
267
|
-
console.error(' [DEBUG] All entries:', entries)
|
|
268
|
-
throw new Error('No folder found in extracted archive')
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
console.log(` [DEBUG] Extracted folder: ${extractedFolder}`)
|
|
272
|
-
console.log(' [DEBUG] Contents of extracted folder:')
|
|
273
|
-
const extractedContents = fs.readdirSync(join(tmpDir, extractedFolder))
|
|
274
|
-
extractedContents.forEach(e => {
|
|
275
|
-
const fullPath = join(tmpDir, extractedFolder, e)
|
|
276
|
-
const stat = fs.statSync(fullPath)
|
|
277
|
-
console.log(` [DEBUG] ${e} - ${stat.isDirectory() ? 'DIR' : 'FILE'} (${stat.size} bytes)`)
|
|
278
|
-
})
|
|
279
242
|
|
|
280
243
|
console.log(` Installing to: ${extractDir}`)
|
|
281
|
-
|
|
282
|
-
console.log(' [DEBUG] Renamed folder to extractDir')
|
|
283
|
-
|
|
284
|
-
console.log(' [DEBUG] Verifying extractDir contents:')
|
|
285
|
-
const installContents = fs.readdirSync(extractDir)
|
|
286
|
-
installContents.forEach(e => {
|
|
287
|
-
const fullPath = join(extractDir, e)
|
|
288
|
-
const stat = fs.statSync(fullPath)
|
|
289
|
-
console.log(` [DEBUG] ${e} - ${stat.isDirectory() ? 'DIR' : 'FILE'} (${stat.size} bytes)`)
|
|
290
|
-
})
|
|
244
|
+
await extractTarGz(filepath, extractDir, 1)
|
|
291
245
|
|
|
292
246
|
rm('-rf', tmpDir)
|
|
293
|
-
console.log(' [DEBUG] Temp directory cleaned')
|
|
294
247
|
|
|
295
248
|
const exePath = getElectermExePath()
|
|
296
|
-
console.log(` [DEBUG] Expected exe path: ${exePath}`)
|
|
297
|
-
console.log(` [DEBUG] Exe exists: ${fs.existsSync(exePath)}`)
|
|
298
249
|
if (!fs.existsSync(exePath)) {
|
|
299
250
|
throw new Error(`electerm.exe not found at ${exePath} after extraction. Archive may have an unexpected structure.`)
|
|
300
251
|
}
|
|
@@ -303,93 +254,38 @@ async function runWin (archName) {
|
|
|
303
254
|
}
|
|
304
255
|
|
|
305
256
|
async function runWin7 () {
|
|
306
|
-
console.log(' [DEBUG] runWin7 started')
|
|
307
|
-
console.log(` [DEBUG] packageRoot: ${packageRoot}`)
|
|
308
|
-
console.log(` [DEBUG] extractDir: ${extractDir}`)
|
|
309
|
-
|
|
310
257
|
const rawVer = await getVer()
|
|
311
258
|
const ver = sanitizeVersion(rawVer)
|
|
312
259
|
|
|
313
|
-
console.log(`
|
|
314
|
-
console.log(` Sanitized version: ${ver}`)
|
|
260
|
+
console.log(` Version: ${ver}`)
|
|
315
261
|
console.log(' Target: win7')
|
|
316
262
|
|
|
317
263
|
const target = join(packageRoot, `electerm-${ver}-win7`)
|
|
318
|
-
console.log(` [DEBUG] Target folder: ${target}`)
|
|
319
264
|
|
|
320
|
-
console.log(' Cleaning old installations...')
|
|
321
265
|
rm('-rf', [target, extractDir])
|
|
322
|
-
|
|
266
|
+
fs.mkdirSync(extractDir, { recursive: true })
|
|
323
267
|
|
|
324
268
|
console.log(' Fetching release info...')
|
|
325
269
|
const releaseInfo = await getReleaseInfo(r => /electerm-\d+\.\d+\.\d+-win7\.tar\.gz$/.test(r.name))
|
|
326
270
|
if (!releaseInfo) {
|
|
327
271
|
throw new Error('No release found for Windows 7')
|
|
328
272
|
}
|
|
329
|
-
console.log(` [DEBUG] Release info found: ${JSON.stringify(releaseInfo, null, 2)}`)
|
|
330
273
|
|
|
331
274
|
const tmpDir = join(packageRoot, '.electerm-tmp')
|
|
332
|
-
console.log(` [DEBUG] Creating temp directory: ${tmpDir}`)
|
|
333
275
|
rm('-rf', tmpDir)
|
|
334
276
|
fs.mkdirSync(tmpDir, { recursive: true })
|
|
335
277
|
|
|
336
278
|
const proxyUrl = applyProxy(releaseInfo.browser_download_url)
|
|
337
|
-
console.log(`
|
|
338
|
-
console.log(` [DEBUG] Download URL: ${releaseInfo.browser_download_url}`)
|
|
279
|
+
console.log(` URL: ${proxyUrl}`)
|
|
339
280
|
|
|
340
|
-
console.log(' Downloading...')
|
|
341
281
|
const { filepath } = await download(releaseInfo.browser_download_url, tmpDir, { extract: false, displayName: releaseInfo.name })
|
|
342
|
-
console.log(` [DEBUG] Downloaded to: ${filepath}`)
|
|
343
|
-
console.log(` [DEBUG] File exists: ${fs.existsSync(filepath)}`)
|
|
344
|
-
console.log(` [DEBUG] File size: ${fs.statSync(filepath).size}`)
|
|
345
|
-
|
|
346
|
-
console.log(' Extracting...')
|
|
347
|
-
await extractTarGz(filepath, tmpDir)
|
|
348
|
-
console.log(' [DEBUG] Extraction complete')
|
|
349
|
-
|
|
350
|
-
console.log(' [DEBUG] Listing temp directory contents:')
|
|
351
|
-
const entries = fs.readdirSync(tmpDir)
|
|
352
|
-
entries.forEach(e => {
|
|
353
|
-
const fullPath = join(tmpDir, e)
|
|
354
|
-
const stat = fs.statSync(fullPath)
|
|
355
|
-
console.log(` [DEBUG] ${e} - ${stat.isDirectory() ? 'DIR' : 'FILE'} (${stat.size} bytes)`)
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
const extractedFolder = entries.find(e => fs.statSync(join(tmpDir, e)).isDirectory())
|
|
359
|
-
|
|
360
|
-
if (!extractedFolder) {
|
|
361
|
-
console.error(' [DEBUG] No directory found in extracted archive')
|
|
362
|
-
console.error(' [DEBUG] All entries:', entries)
|
|
363
|
-
throw new Error('No folder found in extracted archive')
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
console.log(` [DEBUG] Extracted folder: ${extractedFolder}`)
|
|
367
|
-
console.log(' [DEBUG] Contents of extracted folder:')
|
|
368
|
-
const extractedContents = fs.readdirSync(join(tmpDir, extractedFolder))
|
|
369
|
-
extractedContents.forEach(e => {
|
|
370
|
-
const fullPath = join(tmpDir, extractedFolder, e)
|
|
371
|
-
const stat = fs.statSync(fullPath)
|
|
372
|
-
console.log(` [DEBUG] ${e} - ${stat.isDirectory() ? 'DIR' : 'FILE'} (${stat.size} bytes)`)
|
|
373
|
-
})
|
|
374
282
|
|
|
375
283
|
console.log(` Installing to: ${extractDir}`)
|
|
376
|
-
|
|
377
|
-
console.log(' [DEBUG] Renamed folder to extractDir')
|
|
378
|
-
|
|
379
|
-
console.log(' [DEBUG] Verifying extractDir contents:')
|
|
380
|
-
const installContents = fs.readdirSync(extractDir)
|
|
381
|
-
installContents.forEach(e => {
|
|
382
|
-
const fullPath = join(extractDir, e)
|
|
383
|
-
const stat = fs.statSync(fullPath)
|
|
384
|
-
console.log(` [DEBUG] ${e} - ${stat.isDirectory() ? 'DIR' : 'FILE'} (${stat.size} bytes)`)
|
|
385
|
-
})
|
|
284
|
+
await extractTarGz(filepath, extractDir, 1)
|
|
386
285
|
|
|
387
286
|
rm('-rf', tmpDir)
|
|
388
|
-
console.log(' [DEBUG] Temp directory cleaned')
|
|
389
287
|
|
|
390
288
|
const exePath = getElectermExePath()
|
|
391
|
-
console.log(` [DEBUG] Expected exe path: ${exePath}`)
|
|
392
|
-
console.log(` [DEBUG] Exe exists: ${fs.existsSync(exePath)}`)
|
|
393
289
|
if (!fs.existsSync(exePath)) {
|
|
394
290
|
throw new Error(`electerm.exe not found at ${exePath} after extraction. Archive may have an unexpected structure.`)
|
|
395
291
|
}
|