codebuff 1.0.680 → 1.0.681

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 +195 -34
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -74,6 +74,14 @@ function shouldExitAlternateScreen(code, signal) {
74
74
  return Boolean(signal) || isWindowsNativeCrashCode(code)
75
75
  }
76
76
 
77
+ function isIllegalInstructionExit(code, signal) {
78
+ const unsignedCode = getUnsignedExitCode(code)
79
+ return (
80
+ signal === 'SIGILL' ||
81
+ (process.platform === 'win32' && unsignedCode === 0xC000001D)
82
+ )
83
+ }
84
+
77
85
  function createConfig(packageName) {
78
86
  const homeDir = os.homedir()
79
87
  const configDir = path.join(homeDir, '.config', 'manicode')
@@ -164,10 +172,17 @@ function trackUpdateFailed(errorMessage, version, context = {}) {
164
172
 
165
173
  const PLATFORM_TARGETS = {
166
174
  'linux-x64': `${packageName}-linux-x64.tar.gz`,
175
+ 'linux-x64-baseline': `${packageName}-linux-x64-baseline.tar.gz`,
167
176
  'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
168
177
  'darwin-x64': `${packageName}-darwin-x64.tar.gz`,
169
178
  'darwin-arm64': `${packageName}-darwin-arm64.tar.gz`,
170
179
  'win32-x64': `${packageName}-win32-x64.tar.gz`,
180
+ 'win32-x64-baseline': `${packageName}-win32-x64-baseline.tar.gz`,
181
+ }
182
+
183
+ const BASELINE_FALLBACK_TARGETS = {
184
+ 'linux-x64': 'linux-x64-baseline',
185
+ 'win32-x64': 'win32-x64-baseline',
171
186
  }
172
187
 
173
188
  const term = {
@@ -186,6 +201,84 @@ const term = {
186
201
  },
187
202
  }
188
203
 
204
+ function getPlatformKey() {
205
+ return `${process.platform}-${process.arch}`
206
+ }
207
+
208
+ function getTargetOverride() {
209
+ const envNames = [
210
+ `${packageName.toUpperCase()}_BINARY_TARGET`,
211
+ 'CODEBUFF_BINARY_TARGET',
212
+ 'CLI_BINARY_TARGET',
213
+ ]
214
+
215
+ for (const envName of envNames) {
216
+ const target = process.env[envName]
217
+ if (target && PLATFORM_TARGETS[target]) {
218
+ return target
219
+ }
220
+ }
221
+
222
+ return null
223
+ }
224
+
225
+ function linuxCpuHasAvx2() {
226
+ if (process.platform !== 'linux' || process.arch !== 'x64') {
227
+ return true
228
+ }
229
+
230
+ try {
231
+ return /\bavx2\b/i.test(fs.readFileSync('/proc/cpuinfo', 'utf8'))
232
+ } catch {
233
+ return true
234
+ }
235
+ }
236
+
237
+ function getDefaultTargetKey() {
238
+ const override = getTargetOverride()
239
+ if (override) {
240
+ return override
241
+ }
242
+
243
+ const platformKey = getPlatformKey()
244
+ if (platformKey === 'linux-x64' && !linuxCpuHasAvx2()) {
245
+ return 'linux-x64-baseline'
246
+ }
247
+
248
+ return platformKey
249
+ }
250
+
251
+ function getBaselineFallbackTargetKey() {
252
+ // Windows has no reliable plain-Node CPU feature check here, so we keep
253
+ // the fast x64 binary first and fall back after the native SIGILL code.
254
+ return BASELINE_FALLBACK_TARGETS[getPlatformKey()] || null
255
+ }
256
+
257
+ function isTargetAllowedForThisMachine(target) {
258
+ const override = getTargetOverride()
259
+ if (override) {
260
+ return target === override
261
+ }
262
+ return (
263
+ target === getDefaultTargetKey() ||
264
+ target === getBaselineFallbackTargetKey()
265
+ )
266
+ }
267
+
268
+ function getDownloadTargetKey() {
269
+ const override = getTargetOverride()
270
+ if (override) {
271
+ return override
272
+ }
273
+
274
+ const metadata = getCurrentMetadata()
275
+ if (metadata?.target && isTargetAllowedForThisMachine(metadata.target)) {
276
+ return metadata.target
277
+ }
278
+
279
+ return getDefaultTargetKey()
280
+ }
281
+
189
282
  async function getLatestVersion() {
190
283
  try {
191
284
  const res = await httpGet(
@@ -214,20 +307,35 @@ function streamToString(stream) {
214
307
 
215
308
  function getCurrentVersion() {
216
309
  try {
217
- if (!fs.existsSync(CONFIG.metadataPath)) {
310
+ const metadata = getCurrentMetadata()
311
+ if (!metadata) {
218
312
  return null
219
313
  }
220
- const metadata = JSON.parse(fs.readFileSync(CONFIG.metadataPath, 'utf8'))
221
314
  // Also verify the binary still exists
222
315
  if (!fs.existsSync(CONFIG.binaryPath)) {
223
316
  return null
224
317
  }
318
+ const metadataTarget = metadata.target || getPlatformKey()
319
+ if (!isTargetAllowedForThisMachine(metadataTarget)) {
320
+ return null
321
+ }
225
322
  return metadata.version || null
226
323
  } catch (error) {
227
324
  return null
228
325
  }
229
326
  }
230
327
 
328
+ function getCurrentMetadata() {
329
+ try {
330
+ if (!fs.existsSync(CONFIG.metadataPath)) {
331
+ return null
332
+ }
333
+ return JSON.parse(fs.readFileSync(CONFIG.metadataPath, 'utf8'))
334
+ } catch {
335
+ return null
336
+ }
337
+ }
338
+
231
339
  function compareVersions(v1, v2) {
232
340
  if (!v1 || !v2) return 0
233
341
 
@@ -306,13 +414,12 @@ function createProgressBar(percentage, width = 30) {
306
414
  return '[' + '█'.repeat(filled) + '░'.repeat(empty) + ']'
307
415
  }
308
416
 
309
- async function downloadBinary(version) {
310
- const platformKey = `${process.platform}-${process.arch}`
311
- const fileName = PLATFORM_TARGETS[platformKey]
417
+ async function downloadBinary(version, targetKey = getDownloadTargetKey()) {
418
+ const fileName = PLATFORM_TARGETS[targetKey]
312
419
 
313
420
  if (!fileName) {
314
421
  const error = new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
315
- trackUpdateFailed(error.message, version, { stage: 'platform_check' })
422
+ trackUpdateFailed(error.message, version, { stage: 'platform_check', target: targetKey })
316
423
  throw error
317
424
  }
318
425
 
@@ -336,7 +443,7 @@ async function downloadBinary(version) {
336
443
  if (res.statusCode !== 200) {
337
444
  fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
338
445
  const error = new Error(`Download failed: HTTP ${res.statusCode}`)
339
- trackUpdateFailed(error.message, version, { stage: 'http_download', statusCode: res.statusCode })
446
+ trackUpdateFailed(error.message, version, { stage: 'http_download', statusCode: res.statusCode, target: targetKey })
340
447
  throw error
341
448
  }
342
449
 
@@ -380,7 +487,7 @@ async function downloadBinary(version) {
380
487
  const error = new Error(
381
488
  `Binary not found after extraction. Expected: ${CONFIG.binaryName}, Available files: ${files.join(', ')}`,
382
489
  )
383
- trackUpdateFailed(error.message, version, { stage: 'extraction' })
490
+ trackUpdateFailed(error.message, version, { stage: 'extraction', target: targetKey })
384
491
  throw error
385
492
  }
386
493
 
@@ -434,7 +541,7 @@ async function downloadBinary(version) {
434
541
  // Save version metadata for fast version checking
435
542
  fs.writeFileSync(
436
543
  CONFIG.metadataPath,
437
- JSON.stringify({ version }, null, 2),
544
+ JSON.stringify({ version, target: targetKey }, null, 2),
438
545
  )
439
546
  } finally {
440
547
  // Clean up temp directory even if rename fails
@@ -518,14 +625,7 @@ async function checkForUpdates(runningProcess, exitListener) {
518
625
  await downloadBinary(latestVersion)
519
626
 
520
627
  const newChild = spawnInstalledBinary({ detached: false })
521
-
522
- newChild.on('exit', (code, signal) => {
523
- resetTerminal({
524
- exitAlternateScreen: shouldExitAlternateScreen(code, signal),
525
- })
526
- printCrashDiagnostics(code, signal)
527
- process.exit(signal ? 1 : (code || 0))
528
- })
628
+ attachExitHandler(newChild)
529
629
 
530
630
  return new Promise(() => {})
531
631
  }
@@ -537,9 +637,7 @@ async function checkForUpdates(runningProcess, exitListener) {
537
637
  function printCrashDiagnostics(code, signal) {
538
638
  // Windows NTSTATUS codes (unsigned DWORD)
539
639
  const unsignedCode = getUnsignedExitCode(code)
540
- const isIllegalInstruction =
541
- signal === 'SIGILL' ||
542
- (process.platform === 'win32' && unsignedCode === 0xC000001D)
640
+ const isIllegalInstruction = isIllegalInstructionExit(code, signal)
543
641
  const isAccessViolation =
544
642
  signal === 'SIGSEGV' ||
545
643
  (process.platform === 'win32' && unsignedCode === 0xC0000005)
@@ -620,6 +718,11 @@ function printSpawnFailure(err) {
620
718
  console.error('')
621
719
  }
622
720
 
721
+ function exitOnSpawnFailure(err) {
722
+ printSpawnFailure(err)
723
+ process.exit(1)
724
+ }
725
+
623
726
  function spawnInstalledBinary(options = {}) {
624
727
  if (!fs.existsSync(CONFIG.binaryPath)) {
625
728
  try {
@@ -631,29 +734,79 @@ function spawnInstalledBinary(options = {}) {
631
734
  `downloaded binary is missing at ${CONFIG.binaryPath}`,
632
735
  )
633
736
  error.code = 'BINARY_MISSING'
634
- printSpawnFailure(error)
635
- process.exit(1)
737
+ exitOnSpawnFailure(error)
636
738
  }
637
739
 
638
- const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
639
- stdio: 'inherit',
640
- ...options,
641
- })
740
+ // spawn() only emits 'error' asynchronously for a few errno values
741
+ // (EACCES, EAGAIN, EMFILE, ENFILE, ENOENT); everything else — notably
742
+ // UNKNOWN on Windows when antivirus or Smart App Control blocks the
743
+ // exe or the download is corrupt — is thrown synchronously.
744
+ let child
745
+ try {
746
+ child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
747
+ stdio: 'inherit',
748
+ ...options,
749
+ })
750
+ } catch (err) {
751
+ exitOnSpawnFailure(err)
752
+ }
642
753
 
643
- child.on('error', (err) => {
644
- printSpawnFailure(err)
645
- process.exit(1)
646
- })
754
+ child.on('error', exitOnSpawnFailure)
647
755
 
648
756
  return child
649
757
  }
650
758
 
651
- async function main() {
652
- await ensureBinaryExists()
759
+ async function tryFallbackToBaseline(code, signal) {
760
+ if (!isIllegalInstructionExit(code, signal)) {
761
+ return false
762
+ }
653
763
 
654
- const child = spawnInstalledBinary()
764
+ const fallbackTarget = getBaselineFallbackTargetKey()
765
+ if (!fallbackTarget) {
766
+ return false
767
+ }
768
+
769
+ const metadata = getCurrentMetadata()
770
+ const currentTarget = metadata?.target || getDefaultTargetKey()
771
+ if (currentTarget === fallbackTarget) {
772
+ return false
773
+ }
774
+
775
+ const version = metadata?.version || (await getLatestVersion())
776
+ if (!version) {
777
+ return false
778
+ }
779
+
780
+ resetTerminal({
781
+ exitAlternateScreen: shouldExitAlternateScreen(code, signal),
782
+ })
783
+ console.error('')
784
+ console.error(
785
+ `${packageName} is switching to the older-CPU binary for this machine.`,
786
+ )
787
+
788
+ try {
789
+ await downloadBinary(version, fallbackTarget)
790
+ } catch (error) {
791
+ term.clearLine()
792
+ console.error(`Failed to download ${fallbackTarget}: ${error.message}`)
793
+ return false
794
+ }
795
+
796
+ const child = spawnInstalledBinary({ detached: false })
797
+ attachExitHandler(child, false)
798
+ return true
799
+ }
800
+
801
+ function attachExitHandler(child, allowBaselineFallback = true) {
802
+ const exitListener = async (code, signal) => {
803
+ if (
804
+ allowBaselineFallback &&
805
+ (await tryFallbackToBaseline(code, signal))
806
+ ) {
807
+ return
808
+ }
655
809
 
656
- const exitListener = (code, signal) => {
657
810
  resetTerminal({
658
811
  exitAlternateScreen: shouldExitAlternateScreen(code, signal),
659
812
  })
@@ -662,6 +815,14 @@ async function main() {
662
815
  }
663
816
 
664
817
  child.on('exit', exitListener)
818
+ return exitListener
819
+ }
820
+
821
+ async function main() {
822
+ await ensureBinaryExists()
823
+
824
+ const child = spawnInstalledBinary()
825
+ const exitListener = attachExitHandler(child)
665
826
 
666
827
  setTimeout(() => {
667
828
  checkForUpdates(child, exitListener)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebuff",
3
- "version": "1.0.680",
3
+ "version": "1.0.681",
4
4
  "description": "AI coding agent",
5
5
  "license": "MIT",
6
6
  "bin": {