freebuff 0.0.95 → 0.0.98
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/index.js +108 -26
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -17,11 +17,9 @@ const packageName = 'freebuff'
|
|
|
17
17
|
* Terminal escape sequences to reset terminal state after the child process exits.
|
|
18
18
|
* When the binary is SIGKILL'd, it can't clean up its own terminal state.
|
|
19
19
|
* The wrapper (this process) survives and must reset these modes.
|
|
20
|
-
*
|
|
21
|
-
* Keep in sync with TERMINAL_RESET_SEQUENCES in cli/src/utils/renderer-cleanup.ts
|
|
22
20
|
*/
|
|
23
|
-
const
|
|
24
|
-
|
|
21
|
+
const EXIT_ALTERNATE_SCREEN_SEQUENCE = '\x1b[?1049l'
|
|
22
|
+
const SAFE_TERMINAL_RESET_SEQUENCES =
|
|
25
23
|
'\x1b[?1000l' + // Disable X10 mouse mode
|
|
26
24
|
'\x1b[?1002l' + // Disable button event mouse mode
|
|
27
25
|
'\x1b[?1003l' + // Disable any-event mouse mode (all motion)
|
|
@@ -30,7 +28,12 @@ const TERMINAL_RESET_SEQUENCES =
|
|
|
30
28
|
'\x1b[?2004l' + // Disable bracketed paste mode
|
|
31
29
|
'\x1b[?25h' // Show cursor
|
|
32
30
|
|
|
33
|
-
|
|
31
|
+
const FULL_TERMINAL_RESET_SEQUENCES =
|
|
32
|
+
EXIT_ALTERNATE_SCREEN_SEQUENCE + SAFE_TERMINAL_RESET_SEQUENCES
|
|
33
|
+
|
|
34
|
+
function resetTerminal(options = {}) {
|
|
35
|
+
const { exitAlternateScreen = false } = options
|
|
36
|
+
|
|
34
37
|
try {
|
|
35
38
|
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
36
39
|
process.stdin.setRawMode(false)
|
|
@@ -40,13 +43,37 @@ function resetTerminal() {
|
|
|
40
43
|
}
|
|
41
44
|
try {
|
|
42
45
|
if (process.stdout.isTTY) {
|
|
43
|
-
|
|
46
|
+
// Exiting the alternate screen is only safe after an interactive child.
|
|
47
|
+
// Plain CLI paths like --help never enter it, and ?1049l can erase output.
|
|
48
|
+
process.stdout.write(
|
|
49
|
+
exitAlternateScreen
|
|
50
|
+
? FULL_TERMINAL_RESET_SEQUENCES
|
|
51
|
+
: SAFE_TERMINAL_RESET_SEQUENCES,
|
|
52
|
+
)
|
|
44
53
|
}
|
|
45
54
|
} catch {
|
|
46
55
|
// stdout may be closed
|
|
47
56
|
}
|
|
48
57
|
}
|
|
49
58
|
|
|
59
|
+
function getUnsignedExitCode(code) {
|
|
60
|
+
return code != null && code < 0 ? (code >>> 0) : code
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isWindowsNativeCrashCode(code) {
|
|
64
|
+
const unsignedCode = getUnsignedExitCode(code)
|
|
65
|
+
return (
|
|
66
|
+
process.platform === 'win32' &&
|
|
67
|
+
(unsignedCode === 0xC000001D ||
|
|
68
|
+
unsignedCode === 0xC0000005 ||
|
|
69
|
+
unsignedCode === 0xC0000409)
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function shouldExitAlternateScreen(code, signal) {
|
|
74
|
+
return Boolean(signal) || isWindowsNativeCrashCode(code)
|
|
75
|
+
}
|
|
76
|
+
|
|
50
77
|
function createConfig(packageName) {
|
|
51
78
|
const homeDir = os.homedir()
|
|
52
79
|
const configDir = path.join(homeDir, '.config', 'manicode')
|
|
@@ -472,27 +499,21 @@ async function checkForUpdates(runningProcess, exitListener) {
|
|
|
472
499
|
}, 5000)
|
|
473
500
|
})
|
|
474
501
|
|
|
475
|
-
resetTerminal()
|
|
502
|
+
resetTerminal({ exitAlternateScreen: true })
|
|
476
503
|
console.log(`Update available: ${currentVersion} → ${latestVersion}`)
|
|
477
504
|
|
|
478
505
|
await downloadBinary(latestVersion)
|
|
479
506
|
|
|
480
|
-
const newChild =
|
|
481
|
-
stdio: 'inherit',
|
|
482
|
-
detached: false,
|
|
483
|
-
})
|
|
507
|
+
const newChild = spawnInstalledBinary({ detached: false })
|
|
484
508
|
|
|
485
509
|
newChild.on('exit', (code, signal) => {
|
|
486
|
-
resetTerminal(
|
|
510
|
+
resetTerminal({
|
|
511
|
+
exitAlternateScreen: shouldExitAlternateScreen(code, signal),
|
|
512
|
+
})
|
|
487
513
|
printCrashDiagnostics(code, signal)
|
|
488
514
|
process.exit(signal ? 1 : (code || 0))
|
|
489
515
|
})
|
|
490
516
|
|
|
491
|
-
newChild.on('error', (err) => {
|
|
492
|
-
console.error('Failed to start freebuff:', err.message)
|
|
493
|
-
process.exit(1)
|
|
494
|
-
})
|
|
495
|
-
|
|
496
517
|
return new Promise(() => {})
|
|
497
518
|
}
|
|
498
519
|
} catch (error) {
|
|
@@ -502,7 +523,7 @@ async function checkForUpdates(runningProcess, exitListener) {
|
|
|
502
523
|
|
|
503
524
|
function printCrashDiagnostics(code, signal) {
|
|
504
525
|
// Windows NTSTATUS codes (unsigned DWORD)
|
|
505
|
-
const unsignedCode =
|
|
526
|
+
const unsignedCode = getUnsignedExitCode(code)
|
|
506
527
|
const isIllegalInstruction =
|
|
507
528
|
signal === 'SIGILL' ||
|
|
508
529
|
(process.platform === 'win32' && unsignedCode === 0xC000001D)
|
|
@@ -548,26 +569,87 @@ function printCrashDiagnostics(code, signal) {
|
|
|
548
569
|
console.error('')
|
|
549
570
|
}
|
|
550
571
|
|
|
551
|
-
|
|
552
|
-
|
|
572
|
+
function getInstalledBinaryStatus() {
|
|
573
|
+
try {
|
|
574
|
+
const stats = fs.statSync(CONFIG.binaryPath)
|
|
575
|
+
return stats.isFile() ? `yes (${formatBytes(stats.size)})` : 'no'
|
|
576
|
+
} catch {
|
|
577
|
+
return 'no'
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function printSpawnFailure(err) {
|
|
582
|
+
resetTerminal()
|
|
583
|
+
const code = err && err.code ? ` (${err.code})` : ''
|
|
584
|
+
|
|
585
|
+
console.error(`Failed to start ${packageName}: ${err.message}${code}`)
|
|
586
|
+
console.error('')
|
|
587
|
+
console.error('System info:')
|
|
588
|
+
console.error(` Platform: ${process.platform} ${process.arch}`)
|
|
589
|
+
console.error(` Node: ${process.version}`)
|
|
590
|
+
console.error(` Binary: ${CONFIG.binaryPath}`)
|
|
591
|
+
console.error(` Exists: ${getInstalledBinaryStatus()}`)
|
|
592
|
+
|
|
593
|
+
if (process.platform === 'win32') {
|
|
594
|
+
console.error('')
|
|
595
|
+
console.error(
|
|
596
|
+
'On Windows, this can happen when Windows Security or antivirus blocks',
|
|
597
|
+
)
|
|
598
|
+
console.error(
|
|
599
|
+
'or quarantines the downloaded executable, or when the binary requires',
|
|
600
|
+
)
|
|
601
|
+
console.error('CPU instructions that are not available on this machine.')
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
console.error('')
|
|
605
|
+
console.error('Try deleting the downloaded files and running again:')
|
|
606
|
+
console.error(` ${CONFIG.configDir}`)
|
|
607
|
+
console.error('')
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function spawnInstalledBinary(options = {}) {
|
|
611
|
+
if (!fs.existsSync(CONFIG.binaryPath)) {
|
|
612
|
+
try {
|
|
613
|
+
if (fs.existsSync(CONFIG.metadataPath)) fs.unlinkSync(CONFIG.metadataPath)
|
|
614
|
+
} catch {
|
|
615
|
+
// best effort
|
|
616
|
+
}
|
|
617
|
+
const error = new Error(
|
|
618
|
+
`downloaded binary is missing at ${CONFIG.binaryPath}`,
|
|
619
|
+
)
|
|
620
|
+
error.code = 'BINARY_MISSING'
|
|
621
|
+
printSpawnFailure(error)
|
|
622
|
+
process.exit(1)
|
|
623
|
+
}
|
|
553
624
|
|
|
554
625
|
const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
555
626
|
stdio: 'inherit',
|
|
627
|
+
...options,
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
child.on('error', (err) => {
|
|
631
|
+
printSpawnFailure(err)
|
|
632
|
+
process.exit(1)
|
|
556
633
|
})
|
|
557
634
|
|
|
635
|
+
return child
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
async function main() {
|
|
639
|
+
await ensureBinaryExists()
|
|
640
|
+
|
|
641
|
+
const child = spawnInstalledBinary()
|
|
642
|
+
|
|
558
643
|
const exitListener = (code, signal) => {
|
|
559
|
-
resetTerminal(
|
|
644
|
+
resetTerminal({
|
|
645
|
+
exitAlternateScreen: shouldExitAlternateScreen(code, signal),
|
|
646
|
+
})
|
|
560
647
|
printCrashDiagnostics(code, signal)
|
|
561
648
|
process.exit(signal ? 1 : (code || 0))
|
|
562
649
|
}
|
|
563
650
|
|
|
564
651
|
child.on('exit', exitListener)
|
|
565
652
|
|
|
566
|
-
child.on('error', (err) => {
|
|
567
|
-
console.error('Failed to start freebuff:', err.message)
|
|
568
|
-
process.exit(1)
|
|
569
|
-
})
|
|
570
|
-
|
|
571
653
|
setTimeout(() => {
|
|
572
654
|
checkForUpdates(child, exitListener)
|
|
573
655
|
}, 100)
|