freebuff 0.0.136 → 0.0.138

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 (3) hide show
  1. package/index.js +1 -0
  2. package/launcher.js +84 -55
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -24,6 +24,7 @@ const { createLauncher } = require(
24
24
  const launcher = createLauncher({
25
25
  packageName: 'freebuff',
26
26
  displayName: 'Freebuff',
27
+ wrapperVersion: require('./package.json').version,
27
28
  telemetryEvent: 'cli.update_freebuff_failed',
28
29
  })
29
30
 
package/launcher.js CHANGED
@@ -16,6 +16,7 @@ function createLauncher(productConfig) {
16
16
  const {
17
17
  packageName,
18
18
  displayName,
19
+ wrapperVersion = null,
19
20
  includeTreeSitterWasm = true,
20
21
  startupBanner = [],
21
22
  telemetryEvent = 'cli.update_codebuff_failed',
@@ -425,12 +426,18 @@ function createLauncher(productConfig) {
425
426
  if (!isTargetAllowedForThisMachine(metadataTarget)) {
426
427
  return null
427
428
  }
428
- return metadata.version || null
429
+ return getMetadataVersion(metadata)
429
430
  } catch (error) {
430
431
  return null
431
432
  }
432
433
  }
433
434
 
435
+ function getMetadataVersion(metadata) {
436
+ return typeof metadata?.version === 'string' && metadata.version
437
+ ? metadata.version
438
+ : null
439
+ }
440
+
434
441
  function getCurrentMetadata() {
435
442
  try {
436
443
  if (!fs.existsSync(CONFIG.metadataPath)) {
@@ -442,68 +449,63 @@ function createLauncher(productConfig) {
442
449
  }
443
450
  }
444
451
 
445
- function compareVersions(v1, v2) {
446
- if (!v1 || !v2) return 0
452
+ function parseVersion(version) {
453
+ if (typeof version !== 'string') return null
447
454
 
448
- // Always update if the current version is not a valid semver
449
- // e.g. 1.0.420-beta.1
450
- if (!v1.match(/^\d+(\.\d+)*$/)) {
451
- return -1
452
- }
455
+ const match = version.match(
456
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/,
457
+ )
458
+ if (!match) return null
453
459
 
454
- const parseVersion = (version) => {
455
- const parts = version.split('-')
456
- const mainParts = parts[0].split('.').map(Number)
457
- const prereleaseParts = parts[1] ? parts[1].split('.') : []
458
- return { main: mainParts, prerelease: prereleaseParts }
460
+ const prerelease = match[4]?.split('.') ?? []
461
+ if (prerelease.some((part) => /^0\d+$/.test(part))) return null
462
+
463
+ return {
464
+ main: match.slice(1, 4).map(BigInt),
465
+ prerelease,
459
466
  }
467
+ }
460
468
 
469
+ function compareVersions(v1, v2) {
461
470
  const p1 = parseVersion(v1)
462
471
  const p2 = parseVersion(v2)
463
472
 
464
- for (let i = 0; i < Math.max(p1.main.length, p2.main.length); i++) {
465
- const n1 = p1.main[i] || 0
466
- const n2 = p2.main[i] || 0
473
+ // Published package versions are valid semver. Treat malformed cached
474
+ // metadata as stale so the wrapper repairs it instead of trusting it.
475
+ if (!p1) return -1
476
+ if (!p2) return 1
467
477
 
468
- if (n1 < n2) return -1
469
- if (n1 > n2) return 1
478
+ for (let i = 0; i < p1.main.length; i++) {
479
+ if (p1.main[i] < p2.main[i]) return -1
480
+ if (p1.main[i] > p2.main[i]) return 1
470
481
  }
471
482
 
472
- if (p1.prerelease.length === 0 && p2.prerelease.length === 0) {
473
- return 0
474
- } else if (p1.prerelease.length === 0) {
475
- return 1
476
- } else if (p2.prerelease.length === 0) {
477
- return -1
478
- } else {
479
- for (
480
- let i = 0;
481
- i < Math.max(p1.prerelease.length, p2.prerelease.length);
482
- i++
483
- ) {
484
- const pr1 = p1.prerelease[i] || ''
485
- const pr2 = p2.prerelease[i] || ''
486
-
487
- const isNum1 = !isNaN(parseInt(pr1))
488
- const isNum2 = !isNaN(parseInt(pr2))
489
-
490
- if (isNum1 && isNum2) {
491
- const num1 = parseInt(pr1)
492
- const num2 = parseInt(pr2)
493
- if (num1 < num2) return -1
494
- if (num1 > num2) return 1
495
- } else if (isNum1 && !isNum2) {
496
- return 1
497
- } else if (!isNum1 && isNum2) {
498
- return -1
499
- } else if (pr1 < pr2) {
500
- return -1
501
- } else if (pr1 > pr2) {
502
- return 1
503
- }
483
+ if (p1.prerelease.length === 0) {
484
+ return p2.prerelease.length === 0 ? 0 : 1
485
+ }
486
+ if (p2.prerelease.length === 0) return -1
487
+
488
+ for (
489
+ let i = 0;
490
+ i < Math.max(p1.prerelease.length, p2.prerelease.length);
491
+ i++
492
+ ) {
493
+ const identifier1 = p1.prerelease[i]
494
+ const identifier2 = p2.prerelease[i]
495
+ if (identifier1 === undefined) return -1
496
+ if (identifier2 === undefined) return 1
497
+ if (identifier1 === identifier2) continue
498
+
499
+ const numeric1 = /^\d+$/.test(identifier1)
500
+ const numeric2 = /^\d+$/.test(identifier2)
501
+ if (numeric1 && numeric2) {
502
+ return BigInt(identifier1) < BigInt(identifier2) ? -1 : 1
504
503
  }
505
- return 0
504
+ if (numeric1 !== numeric2) return numeric1 ? -1 : 1
505
+ return identifier1 < identifier2 ? -1 : 1
506
506
  }
507
+
508
+ return 0
507
509
  }
508
510
 
509
511
  function formatBytes(bytes) {
@@ -846,13 +848,31 @@ function createLauncher(productConfig) {
846
848
  installStagedBinary(stagedBinary)
847
849
  }
848
850
 
849
- async function ensureBinaryExists() {
851
+ function getRequiredWrapperVersion(currentVersion) {
852
+ if (
853
+ !wrapperVersion ||
854
+ (currentVersion !== null &&
855
+ compareVersions(currentVersion, wrapperVersion) >= 0)
856
+ ) {
857
+ return null
858
+ }
859
+ return wrapperVersion
860
+ }
861
+
862
+ async function ensureBinaryReady() {
850
863
  const currentVersion = getCurrentVersion()
851
- if (currentVersion !== null) {
864
+ const requiredWrapperVersion = getRequiredWrapperVersion(currentVersion)
865
+
866
+ if (currentVersion !== null && requiredWrapperVersion === null) {
852
867
  return
853
868
  }
854
869
 
855
- const version = await getLatestVersion()
870
+ // npm installs update this JavaScript wrapper but intentionally preserve the
871
+ // downloaded binary. If that binary exits before the background update
872
+ // check starts, it can otherwise remain stuck forever. The wrapper and its
873
+ // release binary share a version, so repair that stale cache synchronously
874
+ // without adding a registry lookup to healthy launches.
875
+ const version = requiredWrapperVersion ?? (await getLatestVersion())
856
876
  if (!version) {
857
877
  console.error('❌ Failed to determine latest version')
858
878
  console.error('Please check your internet connection and try again')
@@ -864,6 +884,12 @@ function createLauncher(productConfig) {
864
884
  } catch (error) {
865
885
  term.clearLine()
866
886
  printDownloadFailure(error)
887
+ if (currentVersion !== null) {
888
+ console.error(
889
+ `Continuing with cached ${packageName} ${currentVersion}.`,
890
+ )
891
+ return
892
+ }
867
893
  process.exit(1)
868
894
  }
869
895
  }
@@ -1188,7 +1214,7 @@ function createLauncher(productConfig) {
1188
1214
  }
1189
1215
  if (startupBanner.length > 0) console.log('')
1190
1216
 
1191
- await ensureBinaryExists()
1217
+ await ensureBinaryReady()
1192
1218
 
1193
1219
  const child = spawnInstalledBinary()
1194
1220
  const exitListener = attachExitHandler(child)
@@ -1213,6 +1239,9 @@ function createLauncher(productConfig) {
1213
1239
  getDefaultTargetKey,
1214
1240
  getCpuFeatureCachePath,
1215
1241
  getCurrentVersion,
1242
+ getMetadataVersion,
1243
+ getRequiredWrapperVersion,
1244
+ ensureBinaryReady,
1216
1245
  isTargetAllowedForThisMachine,
1217
1246
  CONFIG,
1218
1247
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freebuff",
3
- "version": "0.0.136",
3
+ "version": "0.0.138",
4
4
  "description": "The world's strongest free coding agent",
5
5
  "license": "MIT",
6
6
  "bin": {