codebuff-cli 1.0.11 → 1.0.14

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.
@@ -3,33 +3,57 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const https = require('https');
5
5
  const http = require('http');
6
- const os = require('os');
7
6
 
8
7
  const REPO = 'Marcus-Mok-GH/codebuff-cli';
9
8
  const BINARY_NAME = 'codebuff';
9
+ const MAX_RETRIES = 3;
10
+ const REQUEST_TIMEOUT_MS = 120000;
11
+ const MAX_REDIRECTS = 10;
10
12
 
11
13
  function getVersion() {
12
- const candidates = [
13
- path.join(__dirname, '..', '..', 'package.json'),
14
- path.join(__dirname, '..', 'package.json'),
15
- ];
16
- for (const p of candidates) {
17
- try {
18
- const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
19
- if (pkg.version) return pkg.version;
20
- } catch {}
21
- }
22
- return '1.0.9';
14
+ const pkgPath = path.join(__dirname, '..', 'package.json');
15
+ try {
16
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
17
+ if (pkg.version) return pkg.version;
18
+ } catch {}
19
+ throw new Error('Could not determine version from ' + pkgPath);
20
+ }
21
+
22
+ function getPlatform() {
23
+ const platform = process.platform;
24
+ const arch = process.arch;
25
+ const mappings = {
26
+ darwin: 'darwin',
27
+ linux: 'linux',
28
+ win32: 'win32',
29
+ };
30
+ const osName = mappings[platform] || platform;
31
+ return osName + '-' + arch;
23
32
  }
24
33
 
25
34
  function getBinaryPath() {
26
- const binDir = path.join(os.homedir(), '.codebuff', 'bin');
27
- const name = process.platform === 'win32' ? `${BINARY_NAME}.exe` : BINARY_NAME;
35
+ const binDir = path.join(__dirname, '..', 'bin');
36
+ const name = process.platform === 'win32' ? BINARY_NAME + '.exe' : BINARY_NAME;
28
37
  return path.join(binDir, name);
29
38
  }
30
39
 
31
- function download(url, dest) {
40
+ function cleanup(dest) {
41
+ try {
42
+ if (fs.existsSync(dest)) {
43
+ fs.unlinkSync(dest);
44
+ }
45
+ } catch {
46
+ // ignore cleanup errors
47
+ }
48
+ }
49
+
50
+ function download(url, dest, redirectCount = 0) {
32
51
  return new Promise((resolve, reject) => {
52
+ if (redirectCount > MAX_REDIRECTS) {
53
+ reject(new Error('Too many redirects (max ' + MAX_REDIRECTS + ') while downloading from ' + url));
54
+ return;
55
+ }
56
+
33
57
  const client = url.startsWith('https:') ? https : http;
34
58
  const file = fs.createWriteStream(dest);
35
59
 
@@ -37,10 +61,10 @@ function download(url, dest) {
37
61
  url,
38
62
  { headers: { 'User-Agent': 'codebuff-cli-installer' } },
39
63
  (res) => {
40
- if (res.statusCode === 301 || res.statusCode === 302) {
64
+ if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307 || res.statusCode === 308) {
41
65
  file.close();
42
- if (fs.existsSync(dest)) fs.unlinkSync(dest);
43
- download(new URL(res.headers.location, url).href, dest)
66
+ cleanup(dest);
67
+ download(new URL(res.headers.location, url).href, dest, redirectCount + 1)
44
68
  .then(resolve)
45
69
  .catch(reject);
46
70
  return;
@@ -48,8 +72,8 @@ function download(url, dest) {
48
72
 
49
73
  if (res.statusCode !== 200) {
50
74
  file.close();
51
- if (fs.existsSync(dest)) fs.unlinkSync(dest);
52
- reject(new Error(`HTTP ${res.statusCode}`));
75
+ cleanup(dest);
76
+ reject(new Error('Download failed: HTTP ' + res.statusCode + ' from ' + url));
53
77
  return;
54
78
  }
55
79
 
@@ -63,67 +87,78 @@ function download(url, dest) {
63
87
 
64
88
  req.on('error', (err) => {
65
89
  file.close();
66
- if (fs.existsSync(dest)) fs.unlinkSync(dest);
67
- reject(err);
90
+ cleanup(dest);
91
+ reject(new Error('Network error downloading from ' + url + ': ' + err.message));
68
92
  });
69
93
 
70
- req.setTimeout(30000, () => {
94
+ req.setTimeout(REQUEST_TIMEOUT_MS, () => {
71
95
  req.destroy();
72
96
  file.close();
73
- if (fs.existsSync(dest)) fs.unlinkSync(dest);
74
- reject(new Error('Request timeout'));
97
+ cleanup(dest);
98
+ reject(new Error('Download timeout (' + REQUEST_TIMEOUT_MS + 'ms) for ' + url));
75
99
  });
76
100
  });
77
101
  }
78
102
 
79
- async function tryDownload(url, dest) {
80
- try {
81
- await download(url, dest);
82
- return true;
83
- } catch {
84
- return false;
103
+ async function downloadWithRetry(url, dest) {
104
+ let lastError;
105
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
106
+ try {
107
+ console.log('Downloading binary (attempt ' + attempt + '/' + MAX_RETRIES + ')...');
108
+ await download(url, dest);
109
+ return true;
110
+ } catch (err) {
111
+ lastError = err;
112
+ console.error('Attempt ' + attempt + ' failed: ' + err.message);
113
+ if (attempt < MAX_RETRIES) {
114
+ const delay = Math.pow(2, attempt) * 1000;
115
+ console.log('Retrying in ' + delay + 'ms...');
116
+ await new Promise((resolve) => setTimeout(resolve, delay));
117
+ }
118
+ }
85
119
  }
120
+ console.error('Failed to download binary after ' + MAX_RETRIES + ' attempts.');
121
+ console.error('Last error: ' + lastError.message);
122
+ return false;
86
123
  }
87
124
 
88
125
  async function main() {
126
+ const version = getVersion();
127
+ const platform = getPlatform();
89
128
  const binaryPath = getBinaryPath();
90
129
 
91
- if (fs.existsSync(binaryPath)) {
92
- console.log('Binary already exists at', binaryPath);
93
- return;
94
- }
95
-
96
- const version = getVersion();
97
- const baseUrl = `https://github.com/${REPO}/releases/download/v${version}`;
130
+ const url = 'https://github.com/' + REPO + '/releases/download/v' + version + '/codebuff-' + platform;
98
131
 
99
- // Try platform-specific binary first
100
- const platformName = `${BINARY_NAME}-${process.platform}-${process.arch}${
101
- process.platform === 'win32' ? '.exe' : ''
102
- }`;
103
- const platformUrl = `${baseUrl}/${platformName}`;
104
- const genericUrl = `${baseUrl}/${BINARY_NAME}`;
132
+ console.log('Platform: ' + platform);
133
+ console.log('Version: ' + version);
134
+ console.log('Binary path: ' + binaryPath);
135
+ console.log('Download URL: ' + url);
105
136
 
106
137
  fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
107
138
 
108
- if (await tryDownload(platformUrl, binaryPath)) {
109
- console.log(`Downloaded platform-specific binary: ${platformName}`);
110
- } else if (await tryDownload(genericUrl, binaryPath)) {
111
- console.log(`Downloaded generic binary: ${BINARY_NAME}`);
139
+ if (fs.existsSync(binaryPath)) {
140
+ console.log('Binary already exists. Skipping download.');
141
+ process.exit(0);
142
+ }
143
+
144
+ if (await downloadWithRetry(url, binaryPath)) {
145
+ console.log('Download complete.');
112
146
  } else {
113
- console.error(
114
- `Failed to download binary from:\n ${platformUrl}\n ${genericUrl}`
115
- );
147
+ console.error('\nUnable to download the codebuff binary.');
148
+ console.error('You can try installing manually from:');
149
+ console.error(' ' + url);
116
150
  process.exit(1);
117
151
  }
118
152
 
119
153
  if (process.platform !== 'win32') {
120
154
  fs.chmodSync(binaryPath, 0o755);
155
+ console.log('Made binary executable.');
121
156
  }
122
157
 
123
- console.log(`Binary installed at: ${binaryPath}`);
158
+ console.log('Binary installed at: ' + binaryPath);
124
159
  }
125
160
 
126
161
  main().catch((err) => {
127
- console.error('Error:', err.message);
162
+ console.error('Fatal error:', err.message);
128
163
  process.exit(1);
129
164
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebuff-cli",
3
- "version": "1.0.11",
3
+ "version": "1.0.14",
4
4
  "license": "Apache-2.0",
5
5
  "bin": {
6
6
  "codebuff": "cli/bin/codebuff.cjs",