codebuff-cli 1.0.19 → 1.0.21

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.
@@ -8,6 +8,7 @@ const http = require('http');
8
8
 
9
9
  const BINARY_NAME = 'codebuff';
10
10
  const REPO = 'Marcus-Mok-GH/codebuff-cli';
11
+ const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases/latest`;
11
12
 
12
13
  const moduleBinary = path.join(__dirname, process.platform === 'win32' ? `${BINARY_NAME}.exe` : BINARY_NAME);
13
14
  const localDir = path.join(os.homedir(), '.codebuff', 'bin');
@@ -50,6 +51,92 @@ function writeVersionFile(version) {
50
51
  }
51
52
  }
52
53
 
54
+ function fetchLatestReleaseTag() {
55
+ return new Promise((resolve, reject) => {
56
+ const client = GITHUB_API_URL.startsWith('https:') ? https : http;
57
+ const req = client.get(
58
+ GITHUB_API_URL,
59
+ {
60
+ headers: {
61
+ 'User-Agent': 'codebuff-cli-installer',
62
+ 'Accept': 'application/vnd.github+json',
63
+ },
64
+ },
65
+ (res) => {
66
+ if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307 || res.statusCode === 308) {
67
+ const redirectUrl = new URL(res.headers.location, GITHUB_API_URL).href;
68
+ // Recursively follow redirect
69
+ const redirectClient = redirectUrl.startsWith('https:') ? https : http;
70
+ const redirectReq = redirectClient.get(
71
+ redirectUrl,
72
+ {
73
+ headers: {
74
+ 'User-Agent': 'codebuff-cli-installer',
75
+ 'Accept': 'application/vnd.github+json',
76
+ },
77
+ },
78
+ (redirectRes) => {
79
+ let data = '';
80
+ redirectRes.on('data', (chunk) => (data += chunk));
81
+ redirectRes.on('end', () => {
82
+ if (redirectRes.statusCode !== 200) {
83
+ reject(new Error(`GitHub API redirect returned HTTP ${redirectRes.statusCode}`));
84
+ return;
85
+ }
86
+ try {
87
+ const json = JSON.parse(data);
88
+ if (json.tag_name) {
89
+ resolve(json.tag_name);
90
+ } else {
91
+ reject(new Error('GitHub API response missing tag_name'));
92
+ }
93
+ } catch (err) {
94
+ reject(new Error(`Failed to parse GitHub API response: ${err.message}`));
95
+ }
96
+ });
97
+ }
98
+ );
99
+ redirectReq.on('error', (err) => reject(new Error(`GitHub API redirect request failed: ${err.message}`)));
100
+ redirectReq.setTimeout(30000, () => {
101
+ redirectReq.destroy();
102
+ reject(new Error('GitHub API redirect request timeout'));
103
+ });
104
+ return;
105
+ }
106
+
107
+ if (res.statusCode !== 200) {
108
+ reject(new Error(`GitHub API returned HTTP ${res.statusCode}`));
109
+ return;
110
+ }
111
+
112
+ let data = '';
113
+ res.on('data', (chunk) => (data += chunk));
114
+ res.on('end', () => {
115
+ try {
116
+ const json = JSON.parse(data);
117
+ if (json.tag_name) {
118
+ resolve(json.tag_name);
119
+ } else {
120
+ reject(new Error('GitHub API response missing tag_name'));
121
+ }
122
+ } catch (err) {
123
+ reject(new Error(`Failed to parse GitHub API response: ${err.message}`));
124
+ }
125
+ });
126
+ }
127
+ );
128
+
129
+ req.on('error', (err) => {
130
+ reject(new Error(`GitHub API request failed: ${err.message}`));
131
+ });
132
+
133
+ req.setTimeout(30000, () => {
134
+ req.destroy();
135
+ reject(new Error('GitHub API request timeout'));
136
+ });
137
+ });
138
+ }
139
+
53
140
  function download(url, dest) {
54
141
  return new Promise((resolve, reject) => {
55
142
  const client = url.startsWith('https:') ? https : http;
@@ -107,13 +194,8 @@ async function tryDownload(url, dest) {
107
194
  }
108
195
  }
109
196
 
110
- async function downloadBinary(destPath) {
111
- const version = getVersion();
112
- if (!version) {
113
- throw new Error('Could not determine version from package.json');
114
- }
115
-
116
- const baseUrl = `https://github.com/${REPO}/releases/download/v${version}`;
197
+ async function downloadBinary(destPath, latestTag) {
198
+ const baseUrl = `https://github.com/${REPO}/releases/download/${latestTag}`;
117
199
  const platformName = `${BINARY_NAME}-${process.platform}-${process.arch}${process.platform === 'win32' ? '.exe' : ''}`;
118
200
  const platformUrl = `${baseUrl}/${platformName}`;
119
201
  const genericUrl = `${baseUrl}/${BINARY_NAME}`;
@@ -151,12 +233,28 @@ function runBinary(binaryPath) {
151
233
  }
152
234
 
153
235
  async function main() {
154
- const pkgVersion = getVersion();
236
+ let latestTag;
237
+ try {
238
+ latestTag = await fetchLatestReleaseTag();
239
+ console.log(`Latest release: ${latestTag}`);
240
+ } catch (err) {
241
+ console.error('Warning: Could not fetch latest release from GitHub:', err.message);
242
+ // Fall back to package.json version if API is unreachable
243
+ const pkgVersion = getVersion();
244
+ if (pkgVersion) {
245
+ latestTag = `v${pkgVersion}`;
246
+ console.log(`Falling back to package version: ${latestTag}`);
247
+ } else {
248
+ console.error('Fatal: Could not determine version. GitHub API is unreachable and no package.json version found.');
249
+ process.exit(1);
250
+ }
251
+ }
252
+
155
253
  const cachedVersion = getCachedVersion();
156
254
 
157
255
  // Check if local cache is stale
158
- if (fs.existsSync(localBinary) && pkgVersion && cachedVersion !== pkgVersion) {
159
- console.log(`Binary cache outdated (${cachedVersion || 'none'} → ${pkgVersion}). Re-downloading...`);
256
+ if (fs.existsSync(localBinary) && cachedVersion !== latestTag) {
257
+ console.log(`Binary cache outdated (${cachedVersion || 'none'} → ${latestTag}). Re-downloading...`);
160
258
  try { fs.unlinkSync(localBinary); } catch {}
161
259
  try { fs.unlinkSync(VERSION_FILE); } catch {}
162
260
  }
@@ -171,8 +269,8 @@ async function main() {
171
269
  console.log('Codebuff binary not found. Downloading...');
172
270
 
173
271
  try {
174
- await downloadBinary(localBinary);
175
- if (pkgVersion) writeVersionFile(pkgVersion);
272
+ await downloadBinary(localBinary, latestTag);
273
+ writeVersionFile(latestTag);
176
274
  binaryPath = localBinary;
177
275
  } catch (err) {
178
276
  console.error('Failed to download codebuff:', err.message);
@@ -6,6 +6,7 @@ const http = require('http');
6
6
 
7
7
  const REPO = 'Marcus-Mok-GH/codebuff-cli';
8
8
  const BINARY_NAME = 'codebuff';
9
+ const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases/latest`;
9
10
  const MAX_RETRIES = 3;
10
11
  const REQUEST_TIMEOUT_MS = 120000;
11
12
  const MAX_REDIRECTS = 10;
@@ -47,6 +48,91 @@ function cleanup(dest) {
47
48
  }
48
49
  }
49
50
 
51
+ function fetchLatestReleaseTag() {
52
+ return new Promise((resolve, reject) => {
53
+ const client = GITHUB_API_URL.startsWith('https:') ? https : http;
54
+ const req = client.get(
55
+ GITHUB_API_URL,
56
+ {
57
+ headers: {
58
+ 'User-Agent': 'codebuff-cli-installer',
59
+ 'Accept': 'application/vnd.github+json',
60
+ },
61
+ },
62
+ (res) => {
63
+ if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307 || res.statusCode === 308) {
64
+ const redirectUrl = new URL(res.headers.location, GITHUB_API_URL).href;
65
+ const redirectClient = redirectUrl.startsWith('https:') ? https : http;
66
+ const redirectReq = redirectClient.get(
67
+ redirectUrl,
68
+ {
69
+ headers: {
70
+ 'User-Agent': 'codebuff-cli-installer',
71
+ 'Accept': 'application/vnd.github+json',
72
+ },
73
+ },
74
+ (redirectRes) => {
75
+ let data = '';
76
+ redirectRes.on('data', (chunk) => (data += chunk));
77
+ redirectRes.on('end', () => {
78
+ if (redirectRes.statusCode !== 200) {
79
+ reject(new Error(`GitHub API redirect returned HTTP ${redirectRes.statusCode}`));
80
+ return;
81
+ }
82
+ try {
83
+ const json = JSON.parse(data);
84
+ if (json.tag_name) {
85
+ resolve(json.tag_name);
86
+ } else {
87
+ reject(new Error('GitHub API response missing tag_name'));
88
+ }
89
+ } catch (err) {
90
+ reject(new Error(`Failed to parse GitHub API response: ${err.message}`));
91
+ }
92
+ });
93
+ }
94
+ );
95
+ redirectReq.on('error', (err) => reject(new Error(`GitHub API redirect request failed: ${err.message}`)));
96
+ redirectReq.setTimeout(30000, () => {
97
+ redirectReq.destroy();
98
+ reject(new Error('GitHub API redirect request timeout'));
99
+ });
100
+ return;
101
+ }
102
+
103
+ if (res.statusCode !== 200) {
104
+ reject(new Error(`GitHub API returned HTTP ${res.statusCode}`));
105
+ return;
106
+ }
107
+
108
+ let data = '';
109
+ res.on('data', (chunk) => (data += chunk));
110
+ res.on('end', () => {
111
+ try {
112
+ const json = JSON.parse(data);
113
+ if (json.tag_name) {
114
+ resolve(json.tag_name);
115
+ } else {
116
+ reject(new Error('GitHub API response missing tag_name'));
117
+ }
118
+ } catch (err) {
119
+ reject(new Error(`Failed to parse GitHub API response: ${err.message}`));
120
+ }
121
+ });
122
+ }
123
+ );
124
+
125
+ req.on('error', (err) => {
126
+ reject(new Error(`GitHub API request failed: ${err.message}`));
127
+ });
128
+
129
+ req.setTimeout(30000, () => {
130
+ req.destroy();
131
+ reject(new Error('GitHub API request timeout'));
132
+ });
133
+ });
134
+ }
135
+
50
136
  function download(url, dest, redirectCount = 0) {
51
137
  return new Promise((resolve, reject) => {
52
138
  if (redirectCount > MAX_REDIRECTS) {
@@ -123,16 +209,28 @@ async function downloadWithRetry(url, dest) {
123
209
  }
124
210
 
125
211
  async function main() {
126
- const version = getVersion();
212
+ let latestTag;
213
+ try {
214
+ latestTag = await fetchLatestReleaseTag();
215
+ console.log('Latest release: ' + latestTag);
216
+ } catch (err) {
217
+ console.error('Warning: Could not fetch latest release from GitHub:', err.message);
218
+ // Fall back to package.json version
219
+ const pkgVersion = getVersion();
220
+ latestTag = 'v' + pkgVersion;
221
+ console.log('Falling back to package version: ' + latestTag);
222
+ }
223
+
127
224
  const platform = getPlatform();
128
225
  const binaryPath = getBinaryPath();
129
226
 
130
- const url = 'https://github.com/' + REPO + '/releases/download/v' + version + '/codebuff-' + platform;
227
+ const baseUrl = 'https://github.com/' + REPO + '/releases/download/' + latestTag;
228
+ const platformUrl = baseUrl + '/codebuff-' + platform;
229
+ const genericUrl = baseUrl + '/codebuff';
131
230
 
132
231
  console.log('Platform: ' + platform);
133
- console.log('Version: ' + version);
232
+ console.log('Version: ' + latestTag);
134
233
  console.log('Binary path: ' + binaryPath);
135
- console.log('Download URL: ' + url);
136
234
 
137
235
  fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
138
236
 
@@ -141,11 +239,25 @@ async function main() {
141
239
  process.exit(0);
142
240
  }
143
241
 
144
- if (await downloadWithRetry(url, binaryPath)) {
242
+ let downloaded = false;
243
+
244
+ console.log('Trying platform-specific URL: ' + platformUrl);
245
+ if (await downloadWithRetry(platformUrl, binaryPath)) {
246
+ console.log('Downloaded platform-specific binary.');
247
+ downloaded = true;
248
+ } else {
249
+ console.log('Platform-specific binary not found. Trying generic URL: ' + genericUrl);
250
+ if (await downloadWithRetry(genericUrl, binaryPath)) {
251
+ console.log('Downloaded generic binary.');
252
+ downloaded = true;
253
+ }
254
+ }
255
+
256
+ if (downloaded) {
145
257
  console.log('Download complete.');
146
258
  const versionFile = path.join(path.dirname(binaryPath), '.version');
147
259
  try {
148
- fs.writeFileSync(versionFile, version, 'utf8');
260
+ fs.writeFileSync(versionFile, latestTag, 'utf8');
149
261
  console.log('Version file written.');
150
262
  } catch (err) {
151
263
  console.warn('Warning: could not write version file:', err.message);
@@ -153,7 +265,8 @@ async function main() {
153
265
  } else {
154
266
  console.error('\nUnable to download the codebuff binary.');
155
267
  console.error('You can try installing manually from:');
156
- console.error(' ' + url);
268
+ console.error(' ' + platformUrl);
269
+ console.error(' ' + genericUrl);
157
270
  process.exit(1);
158
271
  }
159
272
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebuff-cli",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "license": "Apache-2.0",
5
5
  "bin": {
6
6
  "codebuff": "cli/bin/codebuff.cjs",