codebuff 1.0.535 → 1.0.536
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 +118 -76
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -22,6 +22,8 @@ function createConfig(packageName) {
|
|
|
22
22
|
configDir,
|
|
23
23
|
binaryName,
|
|
24
24
|
binaryPath: path.join(configDir, binaryName),
|
|
25
|
+
metadataPath: path.join(configDir, 'codebuff-metadata.json'),
|
|
26
|
+
tempDownloadDir: path.join(configDir, '.download-temp'),
|
|
25
27
|
userAgent: `${packageName}-cli`,
|
|
26
28
|
requestTimeout: 20000,
|
|
27
29
|
}
|
|
@@ -111,52 +113,64 @@ function streamToString(stream) {
|
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
function getCurrentVersion() {
|
|
114
|
-
if (!fs.existsSync(CONFIG.binaryPath)) return null
|
|
115
|
-
|
|
116
116
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
if (!fs.existsSync(CONFIG.metadataPath)) {
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
const metadata = JSON.parse(fs.readFileSync(CONFIG.metadataPath, 'utf8'))
|
|
121
|
+
// Also verify the binary still exists
|
|
122
|
+
if (!fs.existsSync(CONFIG.binaryPath)) {
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
return metadata.version || null
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
}
|
|
122
130
|
|
|
123
|
-
|
|
131
|
+
function runSmokeTest(binaryPath) {
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
if (!fs.existsSync(binaryPath)) {
|
|
134
|
+
resolve(false)
|
|
135
|
+
return
|
|
136
|
+
}
|
|
124
137
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
const child = spawn(binaryPath, ['--version'], {
|
|
139
|
+
cwd: os.homedir(),
|
|
140
|
+
stdio: 'pipe',
|
|
141
|
+
})
|
|
128
142
|
|
|
129
|
-
|
|
130
|
-
// Ignore stderr output
|
|
131
|
-
})
|
|
143
|
+
let output = ''
|
|
132
144
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
child.on('exit', (code) => {
|
|
144
|
-
clearTimeout(timeout)
|
|
145
|
-
if (code === 0) {
|
|
146
|
-
resolve(output.trim())
|
|
147
|
-
} else {
|
|
148
|
-
resolve('error')
|
|
145
|
+
child.stdout.on('data', (data) => {
|
|
146
|
+
output += data.toString()
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const timeout = setTimeout(() => {
|
|
150
|
+
child.kill('SIGTERM')
|
|
151
|
+
setTimeout(() => {
|
|
152
|
+
if (!child.killed) {
|
|
153
|
+
child.kill('SIGKILL')
|
|
149
154
|
}
|
|
150
|
-
})
|
|
155
|
+
}, 1000)
|
|
156
|
+
resolve(false)
|
|
157
|
+
}, 5000)
|
|
158
|
+
|
|
159
|
+
child.on('exit', (code) => {
|
|
160
|
+
clearTimeout(timeout)
|
|
161
|
+
// Check that it exits successfully and outputs something that looks like a version
|
|
162
|
+
if (code === 0 && output.trim().match(/^\d+(\.\d+)*$/)) {
|
|
163
|
+
resolve(true)
|
|
164
|
+
} else {
|
|
165
|
+
resolve(false)
|
|
166
|
+
}
|
|
167
|
+
})
|
|
151
168
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
})
|
|
169
|
+
child.on('error', () => {
|
|
170
|
+
clearTimeout(timeout)
|
|
171
|
+
resolve(false)
|
|
156
172
|
})
|
|
157
|
-
}
|
|
158
|
-
return 'error'
|
|
159
|
-
}
|
|
173
|
+
})
|
|
160
174
|
}
|
|
161
175
|
|
|
162
176
|
function compareVersions(v1, v2) {
|
|
@@ -249,33 +263,21 @@ async function downloadBinary(version) {
|
|
|
249
263
|
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
|
|
250
264
|
}/api/releases/download/${version}/${fileName}`
|
|
251
265
|
|
|
266
|
+
// Ensure config directory exists
|
|
252
267
|
fs.mkdirSync(CONFIG.configDir, { recursive: true })
|
|
253
268
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
} catch (err) {
|
|
258
|
-
// Fallback: try renaming the locked/undeletable binary
|
|
259
|
-
const backupPath = CONFIG.binaryPath + `.old.${Date.now()}`
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
fs.renameSync(CONFIG.binaryPath, backupPath)
|
|
263
|
-
} catch (renameErr) {
|
|
264
|
-
// If we can't unlink OR rename, we can't safely proceed
|
|
265
|
-
throw new Error(
|
|
266
|
-
`Failed to replace existing binary. ` +
|
|
267
|
-
`unlink error: ${err.code || err.message}, ` +
|
|
268
|
-
`rename error: ${renameErr.code || renameErr.message}`,
|
|
269
|
-
)
|
|
270
|
-
}
|
|
271
|
-
}
|
|
269
|
+
// Clean up any previous temp download directory
|
|
270
|
+
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
271
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
272
272
|
}
|
|
273
|
+
fs.mkdirSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
273
274
|
|
|
274
275
|
term.write('Downloading...')
|
|
275
276
|
|
|
276
277
|
const res = await httpGet(downloadUrl)
|
|
277
278
|
|
|
278
279
|
if (res.statusCode !== 200) {
|
|
280
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
279
281
|
throw new Error(`Download failed: HTTP ${res.statusCode}`)
|
|
280
282
|
}
|
|
281
283
|
|
|
@@ -301,31 +303,71 @@ async function downloadBinary(version) {
|
|
|
301
303
|
}
|
|
302
304
|
})
|
|
303
305
|
|
|
306
|
+
// Extract to temp directory
|
|
304
307
|
await new Promise((resolve, reject) => {
|
|
305
308
|
res
|
|
306
309
|
.pipe(zlib.createGunzip())
|
|
307
|
-
.pipe(tar.x({ cwd: CONFIG.
|
|
310
|
+
.pipe(tar.x({ cwd: CONFIG.tempDownloadDir }))
|
|
308
311
|
.on('finish', resolve)
|
|
309
312
|
.on('error', reject)
|
|
310
313
|
})
|
|
311
314
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
+
const tempBinaryPath = path.join(CONFIG.tempDownloadDir, CONFIG.binaryName)
|
|
316
|
+
|
|
317
|
+
// Verify the binary was extracted
|
|
318
|
+
if (!fs.existsSync(tempBinaryPath)) {
|
|
319
|
+
const files = fs.readdirSync(CONFIG.tempDownloadDir)
|
|
320
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
321
|
+
throw new Error(
|
|
322
|
+
`Binary not found after extraction. Expected: ${CONFIG.binaryName}, Available files: ${files.join(', ')}`,
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Set executable permissions
|
|
327
|
+
if (process.platform !== 'win32') {
|
|
328
|
+
fs.chmodSync(tempBinaryPath, 0o755)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Run smoke test on the downloaded binary
|
|
332
|
+
term.write('Verifying download...')
|
|
333
|
+
const smokeTestPassed = await runSmokeTest(tempBinaryPath)
|
|
315
334
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
335
|
+
if (!smokeTestPassed) {
|
|
336
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
337
|
+
throw new Error('Downloaded binary failed smoke test (--version check)')
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Smoke test passed - move binary to final location
|
|
341
|
+
try {
|
|
342
|
+
if (fs.existsSync(CONFIG.binaryPath)) {
|
|
343
|
+
try {
|
|
344
|
+
fs.unlinkSync(CONFIG.binaryPath)
|
|
345
|
+
} catch (err) {
|
|
346
|
+
// Fallback: try renaming the locked/undeletable binary (Windows)
|
|
347
|
+
const backupPath = CONFIG.binaryPath + `.old.${Date.now()}`
|
|
348
|
+
try {
|
|
349
|
+
fs.renameSync(CONFIG.binaryPath, backupPath)
|
|
350
|
+
} catch (renameErr) {
|
|
351
|
+
throw new Error(
|
|
352
|
+
`Failed to replace existing binary. ` +
|
|
353
|
+
`unlink error: ${err.code || err.message}, ` +
|
|
354
|
+
`rename error: ${renameErr.code || renameErr.message}`,
|
|
355
|
+
)
|
|
356
|
+
}
|
|
319
357
|
}
|
|
320
|
-
} else {
|
|
321
|
-
throw new Error(
|
|
322
|
-
`Binary not found after extraction. Expected: ${extractedPath}, Available files: ${files.join(', ')}`,
|
|
323
|
-
)
|
|
324
358
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
359
|
+
fs.renameSync(tempBinaryPath, CONFIG.binaryPath)
|
|
360
|
+
|
|
361
|
+
// Save version metadata for fast version checking
|
|
362
|
+
fs.writeFileSync(
|
|
363
|
+
CONFIG.metadataPath,
|
|
364
|
+
JSON.stringify({ version }, null, 2),
|
|
365
|
+
)
|
|
366
|
+
} finally {
|
|
367
|
+
// Clean up temp directory even if rename fails
|
|
368
|
+
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
369
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
370
|
+
}
|
|
329
371
|
}
|
|
330
372
|
|
|
331
373
|
term.clearLine()
|
|
@@ -333,8 +375,8 @@ async function downloadBinary(version) {
|
|
|
333
375
|
}
|
|
334
376
|
|
|
335
377
|
async function ensureBinaryExists() {
|
|
336
|
-
const currentVersion =
|
|
337
|
-
if (currentVersion !== null
|
|
378
|
+
const currentVersion = getCurrentVersion()
|
|
379
|
+
if (currentVersion !== null) {
|
|
338
380
|
return
|
|
339
381
|
}
|
|
340
382
|
|
|
@@ -357,14 +399,14 @@ async function ensureBinaryExists() {
|
|
|
357
399
|
|
|
358
400
|
async function checkForUpdates(runningProcess, exitListener) {
|
|
359
401
|
try {
|
|
360
|
-
const currentVersion =
|
|
361
|
-
if (!currentVersion) return
|
|
402
|
+
const currentVersion = getCurrentVersion()
|
|
362
403
|
|
|
363
404
|
const latestVersion = await getLatestVersion()
|
|
364
405
|
if (!latestVersion) return
|
|
365
406
|
|
|
366
407
|
if (
|
|
367
|
-
|
|
408
|
+
// Download new version if current version is unknown or outdated.
|
|
409
|
+
currentVersion === null ||
|
|
368
410
|
compareVersions(currentVersion, latestVersion) < 0
|
|
369
411
|
) {
|
|
370
412
|
term.clearLine()
|