codebuff 1.0.512 → 1.0.515

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 +140 -155
  2. package/package.json +1 -2
package/index.js CHANGED
@@ -7,52 +7,52 @@ const os = require('os')
7
7
  const path = require('path')
8
8
  const zlib = require('zlib')
9
9
 
10
- const { Command } = require('commander')
11
10
  const tar = require('tar')
12
11
 
13
- const CONFIG = {
14
- homeDir: os.homedir(),
15
- configDir: path.join(os.homedir(), '.config', 'manicode'),
16
- binaryName: process.platform === 'win32' ? 'codebuff.exe' : 'codebuff',
17
- githubRepo: 'CodebuffAI/codebuff',
18
- userAgent: 'codebuff-cli',
19
- requestTimeout: 20000,
12
+ const packageName = 'codebuff'
13
+
14
+ function createConfig(packageName) {
15
+ const homeDir = os.homedir()
16
+ const configDir = path.join(homeDir, '.config', 'manicode')
17
+ const binaryName =
18
+ process.platform === 'win32' ? `${packageName}.exe` : packageName
19
+
20
+ return {
21
+ homeDir,
22
+ configDir,
23
+ binaryName,
24
+ binaryPath: path.join(configDir, binaryName),
25
+ userAgent: `${packageName}-cli`,
26
+ requestTimeout: 20000,
27
+ }
20
28
  }
21
29
 
22
- CONFIG.binaryPath = path.join(CONFIG.configDir, CONFIG.binaryName)
30
+ const CONFIG = createConfig(packageName)
23
31
 
24
- // Platform target mapping
25
32
  const PLATFORM_TARGETS = {
26
- 'linux-x64': 'codebuff-linux-x64.tar.gz',
27
- 'linux-arm64': 'codebuff-linux-arm64.tar.gz',
28
- 'darwin-x64': 'codebuff-darwin-x64.tar.gz',
29
- 'darwin-arm64': 'codebuff-darwin-arm64.tar.gz',
30
- 'win32-x64': 'codebuff-win32-x64.tar.gz',
33
+ 'linux-x64': `${packageName}-linux-x64.tar.gz`,
34
+ 'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
35
+ 'darwin-x64': `${packageName}-darwin-x64.tar.gz`,
36
+ 'darwin-arm64': `${packageName}-darwin-arm64.tar.gz`,
37
+ 'win32-x64': `${packageName}-win32-x64.tar.gz`,
31
38
  }
32
39
 
33
- // Terminal utilities
34
- let isPrintMode = false
35
40
  const term = {
36
41
  clearLine: () => {
37
- if (!isPrintMode && process.stderr.isTTY) {
42
+ if (process.stderr.isTTY) {
38
43
  process.stderr.write('\r\x1b[K')
39
44
  }
40
45
  },
41
46
  write: (text) => {
42
- if (!isPrintMode) {
43
- term.clearLine()
44
- process.stderr.write(text)
45
- }
47
+ term.clearLine()
48
+ process.stderr.write(text)
46
49
  },
47
50
  writeLine: (text) => {
48
- if (!isPrintMode) {
49
- term.clearLine()
50
- process.stderr.write(text + '\n')
51
- }
51
+ term.clearLine()
52
+ process.stderr.write(text + '\n')
52
53
  },
53
54
  }
54
55
 
55
- // Utility functions
56
56
  function httpGet(url, options = {}) {
57
57
  return new Promise((resolve, reject) => {
58
58
  const parsedUrl = new URL(url)
@@ -65,12 +65,6 @@ function httpGet(url, options = {}) {
65
65
  },
66
66
  }
67
67
 
68
- // Add GitHub token if available
69
- const token = process.env.GITHUB_TOKEN
70
- if (token) {
71
- reqOptions.headers.Authorization = `Bearer ${token}`
72
- }
73
-
74
68
  const req = https.get(reqOptions, (res) => {
75
69
  if (res.statusCode === 302 || res.statusCode === 301) {
76
70
  return httpGet(new URL(res.headers.location, url).href, options)
@@ -117,13 +111,10 @@ function streamToString(stream) {
117
111
  }
118
112
 
119
113
  function getCurrentVersion() {
120
- return new Promise((resolve, reject) => {
121
- try {
122
- if (!fs.existsSync(CONFIG.binaryPath)) {
123
- resolve('error')
124
- return
125
- }
114
+ if (!fs.existsSync(CONFIG.binaryPath)) return null
126
115
 
116
+ try {
117
+ return new Promise((resolve, reject) => {
127
118
  const child = spawn(CONFIG.binaryPath, ['--version'], {
128
119
  cwd: os.homedir(),
129
120
  stdio: 'pipe',
@@ -146,9 +137,9 @@ function getCurrentVersion() {
146
137
  if (!child.killed) {
147
138
  child.kill('SIGKILL')
148
139
  }
149
- }, 1000)
140
+ }, 4000)
150
141
  resolve('error')
151
- }, 1000)
142
+ }, 4000)
152
143
 
153
144
  child.on('exit', (code) => {
154
145
  clearTimeout(timeout)
@@ -163,31 +154,74 @@ function getCurrentVersion() {
163
154
  clearTimeout(timeout)
164
155
  resolve('error')
165
156
  })
166
- } catch (error) {
167
- resolve('error')
168
- }
169
- })
157
+ })
158
+ } catch (error) {
159
+ return 'error'
160
+ }
170
161
  }
171
162
 
172
163
  function compareVersions(v1, v2) {
173
164
  if (!v1 || !v2) return 0
174
165
 
166
+ // Always update if the current version is not a valid semver
167
+ // e.g. 1.0.420-beta.1
175
168
  if (!v1.match(/^\d+(\.\d+)*$/)) {
176
169
  return -1
177
170
  }
178
171
 
179
- const parts1 = v1.split('.').map(Number)
180
- const parts2 = v2.split('.').map(Number)
172
+ const parseVersion = (version) => {
173
+ const parts = version.split('-')
174
+ const mainParts = parts[0].split('.').map(Number)
175
+ const prereleaseParts = parts[1] ? parts[1].split('.') : []
176
+ return { main: mainParts, prerelease: prereleaseParts }
177
+ }
178
+
179
+ const p1 = parseVersion(v1)
180
+ const p2 = parseVersion(v2)
181
181
 
182
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
183
- const p1 = parts1[i] || 0
184
- const p2 = parts2[i] || 0
182
+ for (let i = 0; i < Math.max(p1.main.length, p2.main.length); i++) {
183
+ const n1 = p1.main[i] || 0
184
+ const n2 = p2.main[i] || 0
185
185
 
186
- if (p1 < p2) return -1
187
- if (p1 > p2) return 1
186
+ if (n1 < n2) return -1
187
+ if (n1 > n2) return 1
188
188
  }
189
189
 
190
- return 0
190
+ if (p1.prerelease.length === 0 && p2.prerelease.length === 0) {
191
+ return 0
192
+ } else if (p1.prerelease.length === 0) {
193
+ return 1
194
+ } else if (p2.prerelease.length === 0) {
195
+ return -1
196
+ } else {
197
+ for (
198
+ let i = 0;
199
+ i < Math.max(p1.prerelease.length, p2.prerelease.length);
200
+ i++
201
+ ) {
202
+ const pr1 = p1.prerelease[i] || ''
203
+ const pr2 = p2.prerelease[i] || ''
204
+
205
+ const isNum1 = !isNaN(parseInt(pr1))
206
+ const isNum2 = !isNaN(parseInt(pr2))
207
+
208
+ if (isNum1 && isNum2) {
209
+ const num1 = parseInt(pr1)
210
+ const num2 = parseInt(pr2)
211
+ if (num1 < num2) return -1
212
+ if (num1 > num2) return 1
213
+ } else if (isNum1 && !isNum2) {
214
+ return 1
215
+ } else if (!isNum1 && isNum2) {
216
+ return -1
217
+ } else if (pr1 < pr2) {
218
+ return -1
219
+ } else if (pr1 > pr2) {
220
+ return 1
221
+ }
222
+ }
223
+ return 0
224
+ }
191
225
  }
192
226
 
193
227
  function formatBytes(bytes) {
@@ -212,16 +246,30 @@ async function downloadBinary(version) {
212
246
  throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
213
247
  }
214
248
 
215
- // Use proxy endpoint that handles version mapping
216
- const downloadUrl = process.env.NEXT_PUBLIC_CODEBUFF_APP_URL
217
- ? `${process.env.NEXT_PUBLIC_CODEBUFF_APP_URL}/api/releases/download/${version}/${fileName}`
218
- : `https://codebuff.com/api/releases/download/${version}/${fileName}`
249
+ const downloadUrl = `${
250
+ process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
251
+ }/api/releases/download/${version}/${fileName}`
219
252
 
220
- // Ensure config directory exists
221
253
  fs.mkdirSync(CONFIG.configDir, { recursive: true })
222
254
 
223
255
  if (fs.existsSync(CONFIG.binaryPath)) {
224
- fs.unlinkSync(CONFIG.binaryPath)
256
+ try {
257
+ fs.unlinkSync(CONFIG.binaryPath)
258
+ } catch (err) {
259
+ // Fallback: try renaming the locked/undeletable binary
260
+ const backupPath = CONFIG.binaryPath + `.old.${Date.now()}`
261
+
262
+ try {
263
+ fs.renameSync(CONFIG.binaryPath, backupPath)
264
+ } catch (renameErr) {
265
+ // If we can't unlink OR rename, we can't safely proceed
266
+ throw new Error(
267
+ `Failed to replace existing binary. ` +
268
+ `unlink error: ${err.code || err.message}, ` +
269
+ `rename error: ${renameErr.code || renameErr.message}`,
270
+ )
271
+ }
272
+ }
225
273
  }
226
274
 
227
275
  term.write('Downloading...')
@@ -263,7 +311,6 @@ async function downloadBinary(version) {
263
311
  })
264
312
 
265
313
  try {
266
- // Find the extracted binary - it should be named "codebuff" or "codebuff.exe"
267
314
  const files = fs.readdirSync(CONFIG.configDir)
268
315
  const extractedPath = path.join(CONFIG.configDir, CONFIG.binaryName)
269
316
 
@@ -278,20 +325,12 @@ async function downloadBinary(version) {
278
325
  }
279
326
  } catch (error) {
280
327
  term.clearLine()
281
- if (!isPrintMode) {
282
- console.error(`Extraction failed: ${error.message}`)
283
- }
328
+ console.error(`Extraction failed: ${error.message}`)
284
329
  process.exit(1)
285
330
  }
286
331
 
287
332
  term.clearLine()
288
- if (isPrintMode) {
289
- console.log(
290
- JSON.stringify({ type: 'download', version, status: 'complete' }),
291
- )
292
- } else {
293
- console.log('Download complete! Starting Codebuff...')
294
- }
333
+ console.log('Download complete! Starting Codebuff...')
295
334
  }
296
335
 
297
336
  async function ensureBinaryExists() {
@@ -302,17 +341,8 @@ async function ensureBinaryExists() {
302
341
 
303
342
  const version = await getLatestVersion()
304
343
  if (!version) {
305
- if (isPrintMode) {
306
- console.error(
307
- JSON.stringify({
308
- type: 'error',
309
- message: 'Failed to determine latest version.',
310
- }),
311
- )
312
- } else {
313
- console.error('❌ Failed to determine latest version')
314
- console.error('Please check your internet connection and try again')
315
- }
344
+ console.error('❌ Failed to determine latest version')
345
+ console.error('Please check your internet connection and try again')
316
346
  process.exit(1)
317
347
  }
318
348
 
@@ -320,45 +350,31 @@ async function ensureBinaryExists() {
320
350
  await downloadBinary(version)
321
351
  } catch (error) {
322
352
  term.clearLine()
323
- if (isPrintMode) {
324
- console.error(
325
- JSON.stringify({
326
- type: 'error',
327
- message: `Failed to download codebuff: ${error.message}`,
328
- }),
329
- )
330
- } else {
331
- console.error('❌ Failed to download codebuff:', error.message)
332
- console.error('Please check your internet connection and try again')
333
- }
353
+ console.error('❌ Failed to download codebuff:', error.message)
354
+ console.error('Please check your internet connection and try again')
334
355
  process.exit(1)
335
356
  }
336
357
  }
337
358
 
338
- async function checkForUpdates(runningProcess, exitListener, retry) {
359
+ async function checkForUpdates(runningProcess, exitListener) {
339
360
  try {
340
361
  const currentVersion = await getCurrentVersion()
362
+ if (!currentVersion) return
341
363
 
342
364
  const latestVersion = await getLatestVersion()
343
365
  if (!latestVersion) return
344
366
 
345
367
  if (
346
- // Download new version if current binary errors.
347
368
  currentVersion === 'error' ||
348
369
  compareVersions(currentVersion, latestVersion) < 0
349
370
  ) {
350
371
  term.clearLine()
351
372
 
352
- // Remove the specific exit listener to prevent it from interfering with the update
353
373
  runningProcess.removeListener('exit', exitListener)
354
-
355
- // Kill the running process
356
374
  runningProcess.kill('SIGTERM')
357
375
 
358
- // Wait for the process to actually exit
359
376
  await new Promise((resolve) => {
360
377
  runningProcess.on('exit', resolve)
361
- // Fallback timeout in case the process doesn't exit gracefully
362
378
  setTimeout(() => {
363
379
  if (!runningProcess.killed) {
364
380
  runningProcess.kill('SIGKILL')
@@ -367,76 +383,45 @@ async function checkForUpdates(runningProcess, exitListener, retry) {
367
383
  }, 5000)
368
384
  })
369
385
 
370
- if (!isPrintMode) {
371
- console.log(`Update available: ${currentVersion} → ${latestVersion}`)
372
- }
386
+ console.log(`Update available: ${currentVersion} → ${latestVersion}`)
373
387
 
374
388
  await downloadBinary(latestVersion)
375
389
 
376
- await retry(isPrintMode)
390
+ const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), {
391
+ stdio: 'inherit',
392
+ detached: false,
393
+ })
394
+
395
+ newChild.on('exit', (code) => {
396
+ process.exit(code || 0)
397
+ })
398
+
399
+ return new Promise(() => {})
377
400
  }
378
401
  } catch (error) {
379
- // Silently ignore update check errors
402
+ // Ignore update failures
380
403
  }
381
404
  }
382
405
 
383
- async function main(firstRun = false, printMode = false) {
384
- isPrintMode = printMode
406
+ async function main() {
385
407
  await ensureBinaryExists()
386
408
 
387
- let error = null
388
- try {
389
- // Start codebuff
390
- const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
391
- stdio: 'inherit',
392
- })
409
+ const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
410
+ stdio: 'inherit',
411
+ })
393
412
 
394
- // Store reference to the exit listener so we can remove it during updates
395
- const exitListener = (code) => {
396
- process.exit(code || 0)
397
- }
413
+ const exitListener = (code) => {
414
+ process.exit(code || 0)
415
+ }
398
416
 
399
- child.on('exit', exitListener)
417
+ child.on('exit', exitListener)
400
418
 
401
- if (firstRun) {
402
- // Check for updates in background
403
- setTimeout(() => {
404
- if (!error) {
405
- checkForUpdates(child, exitListener, () => main(false, isPrintMode))
406
- }
407
- }, 100)
408
- }
409
- } catch (err) {
410
- error = err
411
- if (firstRun) {
412
- if (!isPrintMode) {
413
- console.error('❌ Codebuff failed to start:', error.message)
414
- console.log('Redownloading Codebuff...')
415
- }
416
- // Binary could be corrupted (killed before download completed), so delete and retry.
417
- fs.unlinkSync(CONFIG.binaryPath)
418
- await main(false, isPrintMode)
419
- }
420
- }
419
+ setTimeout(() => {
420
+ checkForUpdates(child, exitListener)
421
+ }, 100)
421
422
  }
422
423
 
423
- // Setup commander
424
- const program = new Command()
425
- program
426
- .name('codebuff')
427
- .description('AI coding agent')
428
- .helpOption(false)
429
- .option('-p, --print', 'print mode - suppress wrapper output')
430
- .allowUnknownOption()
431
- .parse()
432
-
433
- const options = program.opts()
434
- isPrintMode = options.print
435
-
436
- // Run the main function
437
- main(true, isPrintMode).catch((error) => {
438
- if (!isPrintMode) {
439
- console.error('❌ Unexpected error:', error.message)
440
- }
424
+ main().catch((error) => {
425
+ console.error('❌ Unexpected error:', error.message)
441
426
  process.exit(1)
442
427
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebuff",
3
- "version": "1.0.512",
3
+ "version": "1.0.515",
4
4
  "description": "AI coding agent",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -27,7 +27,6 @@
27
27
  "node": ">=16"
28
28
  },
29
29
  "dependencies": {
30
- "commander": "^12.0.0",
31
30
  "tar": "^6.2.0"
32
31
  },
33
32
  "repository": {