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.
- package/index.js +130 -18
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
245
|
-
|
|
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
|
-
//
|
|
253
|
-
//
|
|
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 ===
|
|
264
|
-
target ===
|
|
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
|
-
|
|
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
|
-
|
|
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('')
|