codebuff-cli 1.0.19 → 1.0.20
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 +101 -4
- 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,14 +209,25 @@ 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 url = 'https://github.com/' + REPO + '/releases/download/
|
|
227
|
+
const url = 'https://github.com/' + REPO + '/releases/download/' + latestTag + '/codebuff-' + platform;
|
|
131
228
|
|
|
132
229
|
console.log('Platform: ' + platform);
|
|
133
|
-
console.log('Version: ' +
|
|
230
|
+
console.log('Version: ' + latestTag);
|
|
134
231
|
console.log('Binary path: ' + binaryPath);
|
|
135
232
|
console.log('Download URL: ' + url);
|
|
136
233
|
|
|
@@ -145,7 +242,7 @@ async function main() {
|
|
|
145
242
|
console.log('Download complete.');
|
|
146
243
|
const versionFile = path.join(path.dirname(binaryPath), '.version');
|
|
147
244
|
try {
|
|
148
|
-
fs.writeFileSync(versionFile,
|
|
245
|
+
fs.writeFileSync(versionFile, latestTag, 'utf8');
|
|
149
246
|
console.log('Version file written.');
|
|
150
247
|
} catch (err) {
|
|
151
248
|
console.warn('Warning: could not write version file:', err.message);
|