codebuff 1.0.678 → 1.0.679

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 (2) hide show
  1. package/index.js +108 -26
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -17,11 +17,9 @@ const packageName = 'codebuff'
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 TERMINAL_RESET_SEQUENCES =
24
- '\x1b[?1049l' + // Exit alternate screen buffer
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
- function resetTerminal() {
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
- process.stdout.write(TERMINAL_RESET_SEQUENCES)
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')
@@ -485,27 +512,21 @@ async function checkForUpdates(runningProcess, exitListener) {
485
512
  }, 5000)
486
513
  })
487
514
 
488
- resetTerminal()
515
+ resetTerminal({ exitAlternateScreen: true })
489
516
  console.log(`Update available: ${currentVersion} → ${latestVersion}`)
490
517
 
491
518
  await downloadBinary(latestVersion)
492
519
 
493
- const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), {
494
- stdio: 'inherit',
495
- detached: false,
496
- })
520
+ const newChild = spawnInstalledBinary({ detached: false })
497
521
 
498
522
  newChild.on('exit', (code, signal) => {
499
- resetTerminal()
523
+ resetTerminal({
524
+ exitAlternateScreen: shouldExitAlternateScreen(code, signal),
525
+ })
500
526
  printCrashDiagnostics(code, signal)
501
527
  process.exit(signal ? 1 : (code || 0))
502
528
  })
503
529
 
504
- newChild.on('error', (err) => {
505
- console.error('Failed to start codebuff:', err.message)
506
- process.exit(1)
507
- })
508
-
509
530
  return new Promise(() => {})
510
531
  }
511
532
  } catch (error) {
@@ -515,7 +536,7 @@ async function checkForUpdates(runningProcess, exitListener) {
515
536
 
516
537
  function printCrashDiagnostics(code, signal) {
517
538
  // Windows NTSTATUS codes (unsigned DWORD)
518
- const unsignedCode = code != null && code < 0 ? (code >>> 0) : code
539
+ const unsignedCode = getUnsignedExitCode(code)
519
540
  const isIllegalInstruction =
520
541
  signal === 'SIGILL' ||
521
542
  (process.platform === 'win32' && unsignedCode === 0xC000001D)
@@ -561,26 +582,87 @@ function printCrashDiagnostics(code, signal) {
561
582
  console.error('')
562
583
  }
563
584
 
564
- async function main() {
565
- await ensureBinaryExists()
585
+ function getInstalledBinaryStatus() {
586
+ try {
587
+ const stats = fs.statSync(CONFIG.binaryPath)
588
+ return stats.isFile() ? `yes (${formatBytes(stats.size)})` : 'no'
589
+ } catch {
590
+ return 'no'
591
+ }
592
+ }
593
+
594
+ function printSpawnFailure(err) {
595
+ resetTerminal()
596
+ const code = err && err.code ? ` (${err.code})` : ''
597
+
598
+ console.error(`Failed to start ${packageName}: ${err.message}${code}`)
599
+ console.error('')
600
+ console.error('System info:')
601
+ console.error(` Platform: ${process.platform} ${process.arch}`)
602
+ console.error(` Node: ${process.version}`)
603
+ console.error(` Binary: ${CONFIG.binaryPath}`)
604
+ console.error(` Exists: ${getInstalledBinaryStatus()}`)
605
+
606
+ if (process.platform === 'win32') {
607
+ console.error('')
608
+ console.error(
609
+ 'On Windows, this can happen when Windows Security or antivirus blocks',
610
+ )
611
+ console.error(
612
+ 'or quarantines the downloaded executable, or when the binary requires',
613
+ )
614
+ console.error('CPU instructions that are not available on this machine.')
615
+ }
616
+
617
+ console.error('')
618
+ console.error('Try deleting the downloaded files and running again:')
619
+ console.error(` ${CONFIG.configDir}`)
620
+ console.error('')
621
+ }
622
+
623
+ function spawnInstalledBinary(options = {}) {
624
+ if (!fs.existsSync(CONFIG.binaryPath)) {
625
+ try {
626
+ if (fs.existsSync(CONFIG.metadataPath)) fs.unlinkSync(CONFIG.metadataPath)
627
+ } catch {
628
+ // best effort
629
+ }
630
+ const error = new Error(
631
+ `downloaded binary is missing at ${CONFIG.binaryPath}`,
632
+ )
633
+ error.code = 'BINARY_MISSING'
634
+ printSpawnFailure(error)
635
+ process.exit(1)
636
+ }
566
637
 
567
638
  const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
568
639
  stdio: 'inherit',
640
+ ...options,
641
+ })
642
+
643
+ child.on('error', (err) => {
644
+ printSpawnFailure(err)
645
+ process.exit(1)
569
646
  })
570
647
 
648
+ return child
649
+ }
650
+
651
+ async function main() {
652
+ await ensureBinaryExists()
653
+
654
+ const child = spawnInstalledBinary()
655
+
571
656
  const exitListener = (code, signal) => {
572
- resetTerminal()
657
+ resetTerminal({
658
+ exitAlternateScreen: shouldExitAlternateScreen(code, signal),
659
+ })
573
660
  printCrashDiagnostics(code, signal)
574
661
  process.exit(signal ? 1 : (code || 0))
575
662
  }
576
663
 
577
664
  child.on('exit', exitListener)
578
665
 
579
- child.on('error', (err) => {
580
- console.error('Failed to start codebuff:', err.message)
581
- process.exit(1)
582
- })
583
-
584
666
  setTimeout(() => {
585
667
  checkForUpdates(child, exitListener)
586
668
  }, 100)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebuff",
3
- "version": "1.0.678",
3
+ "version": "1.0.679",
4
4
  "description": "AI coding agent",
5
5
  "license": "MIT",
6
6
  "bin": {