freebuff 0.0.115 → 0.0.118

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