freebuff 0.0.114 → 0.0.117

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 +130 -18
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawn } = require('child_process')
3
+ const { spawn, execFileSync } = require('child_process')
4
4
  const fs = require('fs')
5
5
  const http = require('http')
6
6
  const https = require('https')
@@ -223,15 +223,99 @@ function getTargetOverride() {
223
223
  }
224
224
 
225
225
  function linuxCpuHasAvx2() {
226
- if (process.platform !== 'linux' || process.arch !== 'x64') {
226
+ try {
227
+ return /\bavx2\b/i.test(fs.readFileSync('/proc/cpuinfo', 'utf8'))
228
+ } catch {
227
229
  return true
228
230
  }
231
+ }
229
232
 
233
+ // Returns true (AVX2 present), false (absent), or null (couldn't determine).
234
+ // Ask the OS directly via IsProcessorFeaturePresent (kernel32), which is
235
+ // backed by CPUID — far more reliable than matching CPU model names, and it
236
+ // works on the stock Windows PowerShell that ships with every supported
237
+ // Windows version. Feature 40 = PF_AVX2_INSTRUCTIONS_AVAILABLE.
238
+ function probeWindowsAvx2() {
239
+ const script =
240
+ "$f = Add-Type -MemberDefinition '[DllImport(\"kernel32.dll\")] " +
241
+ "public static extern bool IsProcessorFeaturePresent(uint feature);' " +
242
+ "-Name Cpu -Namespace Win32 -PassThru; $f::IsProcessorFeaturePresent(40)"
230
243
  try {
231
- return /\bavx2\b/i.test(fs.readFileSync('/proc/cpuinfo', 'utf8'))
244
+ const out = execFileSync(
245
+ 'powershell.exe',
246
+ ['-NoProfile', '-NonInteractive', '-Command', script],
247
+ { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] },
248
+ ).trim()
249
+ if (out === 'True') return true
250
+ if (out === 'False') return false
251
+ return null
232
252
  } catch {
253
+ // No PowerShell, locked-down policy, timeout, etc. — inconclusive.
254
+ return null
255
+ }
256
+ }
257
+
258
+ let _hasAvx2Cache
259
+
260
+ function machineHasAvx2() {
261
+ if (_hasAvx2Cache === undefined) {
262
+ _hasAvx2Cache = detectMachineHasAvx2()
263
+ }
264
+ return _hasAvx2Cache
265
+ }
266
+
267
+ function detectMachineHasAvx2() {
268
+ if (process.arch !== 'x64') {
233
269
  return true
234
270
  }
271
+
272
+ // Linux detection is a cheap file read, so we don't bother persisting it.
273
+ if (process.platform === 'linux') {
274
+ return linuxCpuHasAvx2()
275
+ }
276
+
277
+ if (process.platform !== 'win32') {
278
+ return true
279
+ }
280
+
281
+ // Windows detection shells out to PowerShell. getDefaultTargetKey runs on
282
+ // every launch (via the version check), so cache the result on disk to keep
283
+ // startup fast after the first probe.
284
+ const cached = readCachedAvx2()
285
+ if (cached !== null) {
286
+ return cached
287
+ }
288
+ const detected = probeWindowsAvx2()
289
+ if (detected === null) {
290
+ // Inconclusive probe: assume AVX2 for this launch and rely on the SIGILL
291
+ // fallback, but don't persist it — a transient failure must not lock in a
292
+ // wrong answer for the lifetime of the install. We'll re-probe next launch.
293
+ return true
294
+ }
295
+ writeCachedAvx2(detected)
296
+ return detected
297
+ }
298
+
299
+ function getCpuFeatureCachePath() {
300
+ return path.join(CONFIG.configDir, 'cpu-features.json')
301
+ }
302
+
303
+ function readCachedAvx2() {
304
+ try {
305
+ const cache = JSON.parse(fs.readFileSync(getCpuFeatureCachePath(), 'utf8'))
306
+ return typeof cache.avx2 === 'boolean' ? cache.avx2 : null
307
+ } catch {
308
+ return null
309
+ }
310
+ }
311
+
312
+ function writeCachedAvx2(value) {
313
+ try {
314
+ fs.mkdirSync(CONFIG.configDir, { recursive: true })
315
+ fs.writeFileSync(getCpuFeatureCachePath(), JSON.stringify({ avx2: value }))
316
+ } catch {
317
+ // Best effort; we'll just re-probe next launch.
318
+ }
235
319
  }
236
320
 
237
321
  function getDefaultTargetKey() {
@@ -241,16 +325,26 @@ function getDefaultTargetKey() {
241
325
  }
242
326
 
243
327
  const platformKey = getPlatformKey()
244
- if (platformKey === 'linux-x64' && !linuxCpuHasAvx2()) {
245
- return 'linux-x64-baseline'
328
+ // Select the binary up front from explicit CPU feature detection rather than
329
+ // optimistically launching the AVX2 build and waiting for it to crash with
330
+ // an illegal instruction. The crash isn't always a clean immediate failure —
331
+ // it can surface later from a deeper code path — so older CPUs (e.g. an
332
+ // Intel Xeon with AVX but no AVX2) are safer on baseline from the start.
333
+ //
334
+ // This assumes every baseline target is gated on AVX2 specifically, which
335
+ // holds today (only linux-x64 and win32-x64 have baseline builds, both
336
+ // AVX2-gated). If a baseline build is ever added for a different reason, give
337
+ // BASELINE_FALLBACK_TARGETS a per-target capability and check that instead.
338
+ if (BASELINE_FALLBACK_TARGETS[platformKey] && !machineHasAvx2()) {
339
+ return BASELINE_FALLBACK_TARGETS[platformKey]
246
340
  }
247
341
 
248
342
  return platformKey
249
343
  }
250
344
 
251
345
  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.
346
+ // Runtime safety net: if proactive detection was unavailable or wrong and the
347
+ // optimized binary still dies with SIGILL, fall back to baseline.
254
348
  return BASELINE_FALLBACK_TARGETS[getPlatformKey()] || null
255
349
  }
256
350
 
@@ -259,9 +353,11 @@ function isTargetAllowedForThisMachine(target) {
259
353
  if (override) {
260
354
  return target === override
261
355
  }
356
+ // Check the baseline fallback first: it's always safe on its platform and
357
+ // avoids running CPU detection when a baseline binary is already installed.
262
358
  return (
263
- target === getDefaultTargetKey() ||
264
- target === getBaselineFallbackTargetKey()
359
+ target === getBaselineFallbackTargetKey() ||
360
+ target === getDefaultTargetKey()
265
361
  )
266
362
  }
267
363
 
@@ -643,8 +739,8 @@ function printCrashDiagnostics(code, signal) {
643
739
  if (isIllegalInstruction) {
644
740
  console.error('Your CPU may not support the required instruction set (AVX2).')
645
741
  console.error('This typically affects CPUs from before 2013.')
646
- console.error('Unfortunately, this binary is not compatible with your system.')
647
742
  console.error('')
743
+ printBaselineOverrideHint()
648
744
  } else if (isAccessViolation) {
649
745
  console.error('The binary crashed with an access violation.')
650
746
  console.error('')
@@ -657,16 +753,33 @@ function printCrashDiagnostics(code, signal) {
657
753
  console.error('')
658
754
  }
659
755
 
660
- console.error('System info:')
661
- console.error(` Platform: ${process.platform} ${process.arch}`)
662
- console.error(` Node: ${process.version}`)
663
- console.error(` Binary: ${CONFIG.binaryPath}`)
756
+ printSystemInfo()
664
757
  console.error('')
665
758
  console.error('Please report this issue at:')
666
759
  console.error(' https://github.com/CodebuffAI/codebuff/issues')
667
760
  console.error('')
668
761
  }
669
762
 
763
+ function printBaselineOverrideHint() {
764
+ const fallbackTarget = getBaselineFallbackTargetKey()
765
+ if (!fallbackTarget) return
766
+ console.error('To force the baseline (non-AVX2) build, set:')
767
+ console.error(` ${packageName.toUpperCase()}_BINARY_TARGET=${fallbackTarget}`)
768
+ console.error('')
769
+ }
770
+
771
+ function printSystemInfo() {
772
+ const metadata = getCurrentMetadata()
773
+ console.error('System info:')
774
+ console.error(` Platform: ${process.platform} ${process.arch}`)
775
+ console.error(` Node: ${process.version}`)
776
+ if (process.arch === 'x64') {
777
+ console.error(` AVX2: ${machineHasAvx2() ? 'yes' : 'no'}`)
778
+ }
779
+ console.error(` Target: ${metadata?.target || getDefaultTargetKey()}`)
780
+ console.error(` Binary: ${CONFIG.binaryPath}`)
781
+ }
782
+
670
783
  function getInstalledBinaryStatus() {
671
784
  try {
672
785
  const stats = fs.statSync(CONFIG.binaryPath)
@@ -682,10 +795,7 @@ function printSpawnFailure(err) {
682
795
 
683
796
  console.error(`Failed to start ${packageName}: ${err.message}${code}`)
684
797
  console.error('')
685
- console.error('System info:')
686
- console.error(` Platform: ${process.platform} ${process.arch}`)
687
- console.error(` Node: ${process.version}`)
688
- console.error(` Binary: ${CONFIG.binaryPath}`)
798
+ printSystemInfo()
689
799
  console.error(` Exists: ${getInstalledBinaryStatus()}`)
690
800
 
691
801
  if (process.platform === 'win32') {
@@ -697,6 +807,8 @@ function printSpawnFailure(err) {
697
807
  'or quarantines the downloaded executable, or when the binary requires',
698
808
  )
699
809
  console.error('CPU instructions that are not available on this machine.')
810
+ console.error('')
811
+ printBaselineOverrideHint()
700
812
  }
701
813
 
702
814
  console.error('')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freebuff",
3
- "version": "0.0.114",
3
+ "version": "0.0.117",
4
4
  "description": "The world's strongest free coding agent",
5
5
  "license": "MIT",
6
6
  "bin": {