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.
- package/cli/bin/codebuff.cjs +110 -12
- package/cli/scripts/download-binary.cjs +120 -7
- package/package.json +1 -1
package/cli/bin/codebuff.cjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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) &&
|
|
159
|
-
console.log(`Binary cache outdated (${cachedVersion || 'none'} → ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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: ' +
|
|
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
|
-
|
|
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,
|
|
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(' ' +
|
|
268
|
+
console.error(' ' + platformUrl);
|
|
269
|
+
console.error(' ' + genericUrl);
|
|
157
270
|
process.exit(1);
|
|
158
271
|
}
|
|
159
272
|
|