🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

freebuff

Package Overview
Dependencies
Maintainers
3
Versions
96
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

freebuff - npm Package Compare versions

Comparing version
0.0.121
to
0.0.122
+44
-2
http.js

@@ -145,2 +145,3 @@ const http = require('http')

async function httpGet(url, options = {}) {
const redirectCount = options.redirectCount || 0
const reqOptions = await buildRequestOptions(url, options)

@@ -150,5 +151,20 @@

const req = httpsModule.get(reqOptions, (res) => {
if (res.statusCode === 301 || res.statusCode === 302) {
if ([301, 302, 303, 307, 308].includes(res.statusCode)) {
res.resume()
httpGet(new URL(res.headers.location, url).href, options)
if (!res.headers.location) {
reject(
new Error(`Redirect ${res.statusCode} missing Location header.`),
)
return
}
if (redirectCount >= (options.maxRedirects ?? 10)) {
reject(new Error('Too many redirects.'))
return
}
httpGet(new URL(res.headers.location, url).href, {
...options,
redirectCount: redirectCount + 1,
})
.then(resolve)

@@ -170,5 +186,31 @@ .catch(reject)

async function withRetries(
operation,
{
maxAttempts = 1,
baseDelayMs = 1000,
shouldRetry = () => true,
onRetry = () => {},
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
},
) {
for (let attempt = 1; ; attempt++) {
try {
return await operation(attempt)
} catch (error) {
if (attempt >= maxAttempts || !shouldRetry(error)) {
throw error
}
const delayMs = baseDelayMs * 2 ** (attempt - 1)
await onRetry({ error, attempt, nextAttempt: attempt + 1, delayMs })
await sleep(delayMs)
}
}
}
return {
getProxyUrl,
httpGet,
withRetries,
}

@@ -175,0 +217,0 @@ }

+119
-63

@@ -9,2 +9,3 @@ #!/usr/bin/env node

const path = require('path')
const { pipeline } = require('stream/promises')
const zlib = require('zlib')

@@ -103,2 +104,4 @@

requestTimeout: 20000,
downloadRequestTimeout: 120000,
downloadMaxAttempts: 3,
}

@@ -108,3 +111,3 @@ }

const CONFIG = createConfig(packageName)
const { getProxyUrl, httpGet } = createReleaseHttpClient({
const { httpGet, withRetries } = createReleaseHttpClient({
env: process.env,

@@ -515,17 +518,17 @@ userAgent: CONFIG.userAgent,

async function downloadBinary(version, targetKey = getDownloadTargetKey()) {
const fileName = PLATFORM_TARGETS[targetKey]
function isRetryableDownloadStatus(statusCode) {
return (
statusCode === 408 ||
statusCode === 425 ||
statusCode === 429 ||
statusCode >= 500
)
}
if (!fileName) {
const error = new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
trackUpdateFailed(error.message, version, { stage: 'platform_check', target: targetKey })
throw error
}
function isRetryableDownloadError(error) {
if (error && typeof error.retryable === 'boolean') return error.retryable
return !['EACCES', 'ENOSPC', 'EPERM', 'EROFS'].includes(error?.code)
}
const downloadUrl = `${
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
}/api/releases/download/${version}/${fileName}`
fs.mkdirSync(CONFIG.configDir, { recursive: true })
function prepareTempDownloadDir() {
if (fs.existsSync(CONFIG.tempDownloadDir)) {

@@ -535,56 +538,119 @@ fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })

fs.mkdirSync(CONFIG.tempDownloadDir, { recursive: true })
}
term.write('Downloading...')
async function downloadAndExtract(downloadUrl, version, targetKey) {
let attempts = 0
const res = await httpGet(downloadUrl)
try {
return await withRetries(
async (attempt) => {
attempts = attempt
prepareTempDownloadDir()
term.write('Downloading...')
if (res.statusCode !== 200) {
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
const error = new Error(`Download failed: HTTP ${res.statusCode}`)
trackUpdateFailed(error.message, version, { stage: 'http_download', statusCode: res.statusCode, target: targetKey })
throw error
}
const res = await httpGet(downloadUrl, {
timeout: CONFIG.downloadRequestTimeout,
})
const totalSize = parseInt(res.headers['content-length'] || '0', 10)
let downloadedSize = 0
let lastProgressTime = Date.now()
if (res.statusCode !== 200) {
res.resume()
const error = new Error(`Download failed: HTTP ${res.statusCode}`)
error.statusCode = res.statusCode
error.retryable = isRetryableDownloadStatus(res.statusCode)
throw error
}
res.on('data', (chunk) => {
downloadedSize += chunk.length
const now = Date.now()
if (now - lastProgressTime >= 100 || downloadedSize === totalSize) {
lastProgressTime = now
if (totalSize > 0) {
const pct = Math.round((downloadedSize / totalSize) * 100)
term.write(
`Downloading... ${createProgressBar(pct)} ${pct}% of ${formatBytes(
totalSize,
)}`,
const totalSize = parseInt(res.headers['content-length'] || '0', 10)
let downloadedSize = 0
let lastProgressTime = Date.now()
res.on('data', (chunk) => {
downloadedSize += chunk.length
const now = Date.now()
if (now - lastProgressTime >= 100 || downloadedSize === totalSize) {
lastProgressTime = now
if (totalSize > 0) {
const pct = Math.round((downloadedSize / totalSize) * 100)
term.write(
`Downloading... ${createProgressBar(pct)} ${pct}% of ${formatBytes(
totalSize,
)}`,
)
} else {
term.write(`Downloading... ${formatBytes(downloadedSize)}`)
}
}
})
await pipeline(
res,
zlib.createGunzip(),
tar.x({ cwd: CONFIG.tempDownloadDir }),
)
} else {
term.write(`Downloading... ${formatBytes(downloadedSize)}`)
}
const tempBinaryPath = path.join(
CONFIG.tempDownloadDir,
CONFIG.binaryName,
)
if (!fs.existsSync(tempBinaryPath)) {
const files = fs.readdirSync(CONFIG.tempDownloadDir)
const error = new Error(
`Binary not found after extraction. Expected: ${CONFIG.binaryName}, Available files: ${files.join(', ')}`,
)
error.retryable = false
throw error
}
return tempBinaryPath
},
{
maxAttempts: CONFIG.downloadMaxAttempts,
shouldRetry: isRetryableDownloadError,
onRetry: ({ nextAttempt, delayMs }) => {
term.writeLine(
`Download interrupted. Retrying in ${delayMs / 1000}s (${nextAttempt}/${CONFIG.downloadMaxAttempts})...`,
)
},
},
)
} catch (error) {
if (fs.existsSync(CONFIG.tempDownloadDir)) {
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
}
})
await new Promise((resolve, reject) => {
res
.pipe(zlib.createGunzip())
.pipe(tar.x({ cwd: CONFIG.tempDownloadDir }))
.on('finish', resolve)
.on('error', reject)
})
trackUpdateFailed(error.message, version, {
stage: 'download',
statusCode: error.statusCode,
target: targetKey,
attempts,
})
throw error
}
}
const tempBinaryPath = path.join(CONFIG.tempDownloadDir, CONFIG.binaryName)
async function downloadBinary(version, targetKey = getDownloadTargetKey()) {
const fileName = PLATFORM_TARGETS[targetKey]
if (!fs.existsSync(tempBinaryPath)) {
const files = fs.readdirSync(CONFIG.tempDownloadDir)
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
if (!fileName) {
const error = new Error(
`Binary not found after extraction. Expected: ${CONFIG.binaryName}, Available files: ${files.join(', ')}`,
`Unsupported platform: ${process.platform} ${process.arch}`,
)
trackUpdateFailed(error.message, version, { stage: 'extraction', target: targetKey })
trackUpdateFailed(error.message, version, {
stage: 'platform_check',
target: targetKey,
})
throw error
}
const downloadUrl = `${
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
}/api/releases/download/${version}/${fileName}`
fs.mkdirSync(CONFIG.configDir, { recursive: true })
const tempBinaryPath = await downloadAndExtract(
downloadUrl,
version,
targetKey,
)
if (process.platform !== 'win32') {

@@ -658,7 +724,2 @@ fs.chmodSync(tempBinaryPath, 0o755)

console.error('Please check your internet connection and try again')
if (!getProxyUrl()) {
console.error(
'If you are behind a proxy, set the HTTPS_PROXY environment variable',
)
}
process.exit(1)

@@ -673,7 +734,2 @@ }

console.error('Please check your internet connection and try again')
if (!getProxyUrl()) {
console.error(
'If you are behind a proxy, set the HTTPS_PROXY environment variable',
)
}
process.exit(1)

@@ -680,0 +736,0 @@ }

{
"name": "freebuff",
"version": "0.0.121",
"version": "0.0.122",
"description": "The world's strongest free coding agent",

@@ -5,0 +5,0 @@ "license": "MIT",