@unitsvc/cc-helper 1.0.6 → 1.0.7
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/bin/cc-helper.js +124 -44
- package/install.js +164 -44
- package/package.json +1 -1
package/bin/cc-helper.js
CHANGED
|
@@ -30,15 +30,99 @@ function handleUninstall() {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
// Get latest release version from GitHub API
|
|
34
|
+
function getLatestVersion() {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const options = {
|
|
37
|
+
hostname: 'api.github.com',
|
|
38
|
+
path: `/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases/latest`,
|
|
39
|
+
headers: {
|
|
40
|
+
'User-Agent': 'cc-helper-npm-installer',
|
|
41
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const req = https.get(options, (res) => {
|
|
46
|
+
let data = '';
|
|
47
|
+
res.on('data', chunk => data += chunk);
|
|
48
|
+
res.on('end', () => {
|
|
49
|
+
try {
|
|
50
|
+
if (res.statusCode !== 200) {
|
|
51
|
+
reject(new Error(`GitHub API returned status ${res.statusCode}`));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const release = JSON.parse(data);
|
|
55
|
+
const version = release.tag_name.replace(/^v/, '');
|
|
56
|
+
resolve(version);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
reject(new Error(`Failed to parse GitHub response: ${err.message}`));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
req.on('error', reject);
|
|
64
|
+
req.setTimeout(10000, () => {
|
|
65
|
+
req.destroy();
|
|
66
|
+
reject(new Error('GitHub API request timeout'));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get all releases from GitHub API
|
|
72
|
+
function getAllReleases() {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const options = {
|
|
75
|
+
hostname: 'api.github.com',
|
|
76
|
+
path: `/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases?per_page=10`,
|
|
77
|
+
headers: {
|
|
78
|
+
'User-Agent': 'cc-helper-npm-installer',
|
|
79
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const req = https.get(options, (res) => {
|
|
84
|
+
let data = '';
|
|
85
|
+
res.on('data', chunk => data += chunk);
|
|
86
|
+
res.on('end', () => {
|
|
87
|
+
try {
|
|
88
|
+
if (res.statusCode !== 200) {
|
|
89
|
+
reject(new Error(`GitHub API returned status ${res.statusCode}`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const releases = JSON.parse(data);
|
|
93
|
+
resolve(releases);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
reject(new Error(`Failed to parse GitHub response: ${err.message}`));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
req.on('error', reject);
|
|
101
|
+
req.setTimeout(10000, () => {
|
|
102
|
+
req.destroy();
|
|
103
|
+
reject(new Error('GitHub API request timeout'));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Find a release with binary assets for the given platform
|
|
109
|
+
async function findReleaseWithBinary(platform, arch) {
|
|
110
|
+
const releases = await getAllReleases();
|
|
111
|
+
|
|
112
|
+
for (const release of releases) {
|
|
113
|
+
const version = release.tag_name.replace(/^v/, '');
|
|
114
|
+
const expectedAsset = platform === 'windows'
|
|
115
|
+
? `cc-helper_${version}_${platform}_${arch}.zip`
|
|
116
|
+
: `cc-helper_${version}_${platform}_${arch}.tar.gz`;
|
|
117
|
+
|
|
118
|
+
const hasBinary = release.assets && release.assets.some(asset => asset.name === expectedAsset);
|
|
119
|
+
|
|
120
|
+
if (hasBinary) {
|
|
121
|
+
return version;
|
|
122
|
+
}
|
|
41
123
|
}
|
|
124
|
+
|
|
125
|
+
throw new Error('No release with binary assets found');
|
|
42
126
|
}
|
|
43
127
|
|
|
44
128
|
function getPlatform() {
|
|
@@ -152,17 +236,10 @@ function extractZip(zipPath, destDir) {
|
|
|
152
236
|
try {
|
|
153
237
|
if (process.platform === 'win32') {
|
|
154
238
|
// Use PowerShell on Windows (built-in, no external dependencies)
|
|
155
|
-
// -
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const psCommand = `
|
|
159
|
-
try {
|
|
160
|
-
Expand-Archive -Path '${zipPath.replace(/'/g, "''")}' -DestinationPath '${destDir.replace(/'/g, "''")}' -Force -ErrorAction Stop
|
|
161
|
-
exit 0
|
|
162
|
-
} catch {
|
|
163
|
-
exit 1
|
|
164
|
-
}
|
|
165
|
-
`;
|
|
239
|
+
// Use single-line command to avoid PowerShell multi-line parsing issues
|
|
240
|
+
const escapedZipPath = zipPath.replace(/'/g, "''");
|
|
241
|
+
const escapedDestDir = destDir.replace(/'/g, "''");
|
|
242
|
+
const psCommand = `Expand-Archive -Path '${escapedZipPath}' -DestinationPath '${escapedDestDir}' -Force -ErrorAction Stop`;
|
|
166
243
|
execSync(`powershell -NoProfile -ExecutionPolicy Bypass -Command "${psCommand}"`, { stdio: 'pipe' });
|
|
167
244
|
} else {
|
|
168
245
|
// Unix/Linux/macOS - use unzip
|
|
@@ -220,32 +297,13 @@ function findBinary(dir, platform) {
|
|
|
220
297
|
throw new Error(`Binary ${binaryName} not found in archive`);
|
|
221
298
|
}
|
|
222
299
|
|
|
223
|
-
async function
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
// Check if binary exists and matches package version
|
|
228
|
-
if (fs.existsSync(binaryPath) && fs.existsSync(VERSION_FILE)) {
|
|
229
|
-
const currentVersion = fs.readFileSync(VERSION_FILE, 'utf8').trim();
|
|
230
|
-
if (currentVersion === packageVersion) {
|
|
231
|
-
return binaryPath;
|
|
232
|
-
}
|
|
233
|
-
console.log(`Updating cc-helper from ${currentVersion} to ${packageVersion}...`);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Create cache directory
|
|
237
|
-
if (!fs.existsSync(CACHE_DIR)) {
|
|
238
|
-
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Download archive with fixed version from package.json
|
|
242
|
-
const { platform, arch } = getPlatform();
|
|
243
|
-
const archiveName = getArchiveName(packageVersion, platform, arch);
|
|
244
|
-
const tagVersion = packageVersion.startsWith('v') ? packageVersion : `v${packageVersion}`;
|
|
300
|
+
async function downloadAndInstall(version, platform, arch, binaryPath) {
|
|
301
|
+
const archiveName = getArchiveName(version, platform, arch);
|
|
302
|
+
const tagVersion = version.startsWith('v') ? version : `v${version}`;
|
|
245
303
|
const downloadUrl = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/releases/download/${tagVersion}/${archiveName}`;
|
|
246
304
|
const archivePath = path.join(CACHE_DIR, archiveName);
|
|
247
305
|
|
|
248
|
-
console.log(`Downloading cc-helper ${
|
|
306
|
+
console.log(`Downloading cc-helper ${version} for ${platform}-${arch}...`);
|
|
249
307
|
|
|
250
308
|
try {
|
|
251
309
|
await downloadFile(downloadUrl, archivePath);
|
|
@@ -274,7 +332,7 @@ async function ensureBinary() {
|
|
|
274
332
|
fs.unlinkSync(archivePath);
|
|
275
333
|
|
|
276
334
|
// Save version
|
|
277
|
-
fs.writeFileSync(VERSION_FILE,
|
|
335
|
+
fs.writeFileSync(VERSION_FILE, version);
|
|
278
336
|
|
|
279
337
|
console.log('Download complete!');
|
|
280
338
|
} catch (err) {
|
|
@@ -284,6 +342,28 @@ async function ensureBinary() {
|
|
|
284
342
|
}
|
|
285
343
|
throw err;
|
|
286
344
|
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function ensureBinary() {
|
|
348
|
+
const binaryPath = getBinaryPath();
|
|
349
|
+
|
|
350
|
+
// If binary exists, use it directly (postinstall already handled version check)
|
|
351
|
+
if (fs.existsSync(binaryPath)) {
|
|
352
|
+
return binaryPath;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Binary doesn't exist, find a compatible release and download
|
|
356
|
+
console.log('Finding compatible release...');
|
|
357
|
+
const { platform, arch } = getPlatform();
|
|
358
|
+
const compatibleVersion = await findReleaseWithBinary(platform, arch);
|
|
359
|
+
|
|
360
|
+
// Create cache directory
|
|
361
|
+
if (!fs.existsSync(CACHE_DIR)) {
|
|
362
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Download and install
|
|
366
|
+
await downloadAndInstall(compatibleVersion, platform, arch, binaryPath);
|
|
287
367
|
|
|
288
368
|
return binaryPath;
|
|
289
369
|
}
|
|
@@ -323,7 +403,7 @@ async function main() {
|
|
|
323
403
|
}
|
|
324
404
|
|
|
325
405
|
// Export for use as module
|
|
326
|
-
module.exports = { main, ensureBinary, getBinaryPath, getPlatform,
|
|
406
|
+
module.exports = { main, ensureBinary, getBinaryPath, getPlatform, getLatestVersion };
|
|
327
407
|
|
|
328
408
|
// Run if called directly
|
|
329
409
|
if (require.main === module) {
|
package/install.js
CHANGED
|
@@ -18,10 +18,110 @@ function getPackageVersion() {
|
|
|
18
18
|
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
19
19
|
return pkg.version;
|
|
20
20
|
} catch (err) {
|
|
21
|
-
|
|
21
|
+
return null;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// Get latest release version from GitHub API
|
|
26
|
+
function getLatestVersion() {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const options = {
|
|
29
|
+
hostname: 'api.github.com',
|
|
30
|
+
path: `/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases/latest`,
|
|
31
|
+
headers: {
|
|
32
|
+
'User-Agent': 'cc-helper-npm-installer',
|
|
33
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const req = https.get(options, (res) => {
|
|
38
|
+
let data = '';
|
|
39
|
+
res.on('data', chunk => data += chunk);
|
|
40
|
+
res.on('end', () => {
|
|
41
|
+
try {
|
|
42
|
+
if (res.statusCode !== 200) {
|
|
43
|
+
reject(new Error(`GitHub API returned status ${res.statusCode}`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const release = JSON.parse(data);
|
|
47
|
+
const version = release.tag_name.replace(/^v/, '');
|
|
48
|
+
resolve(version);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
reject(new Error(`Failed to parse GitHub response: ${err.message}`));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
req.on('error', reject);
|
|
56
|
+
req.setTimeout(10000, () => {
|
|
57
|
+
req.destroy();
|
|
58
|
+
reject(new Error('GitHub API request timeout'));
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Get all releases from GitHub API
|
|
64
|
+
function getAllReleases() {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const options = {
|
|
67
|
+
hostname: 'api.github.com',
|
|
68
|
+
path: `/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases?per_page=10`,
|
|
69
|
+
headers: {
|
|
70
|
+
'User-Agent': 'cc-helper-npm-installer',
|
|
71
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const req = https.get(options, (res) => {
|
|
76
|
+
let data = '';
|
|
77
|
+
res.on('data', chunk => data += chunk);
|
|
78
|
+
res.on('end', () => {
|
|
79
|
+
try {
|
|
80
|
+
if (res.statusCode !== 200) {
|
|
81
|
+
reject(new Error(`GitHub API returned status ${res.statusCode}`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const releases = JSON.parse(data);
|
|
85
|
+
resolve(releases);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
reject(new Error(`Failed to parse GitHub response: ${err.message}`));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
req.on('error', reject);
|
|
93
|
+
req.setTimeout(10000, () => {
|
|
94
|
+
req.destroy();
|
|
95
|
+
reject(new Error('GitHub API request timeout'));
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Find a release with binary assets for the given platform
|
|
101
|
+
async function findReleaseWithBinary(platform, arch) {
|
|
102
|
+
const releases = await getAllReleases();
|
|
103
|
+
const expectedAssetPattern = platform === 'windows'
|
|
104
|
+
? `cc-helper_${arch}.zip`
|
|
105
|
+
: `cc-helper_${arch}.tar.gz`;
|
|
106
|
+
|
|
107
|
+
for (const release of releases) {
|
|
108
|
+
const version = release.tag_name.replace(/^v/, '');
|
|
109
|
+
const expectedAsset = platform === 'windows'
|
|
110
|
+
? `cc-helper_${version}_${platform}_${arch}.zip`
|
|
111
|
+
: `cc-helper_${version}_${platform}_${arch}.tar.gz`;
|
|
112
|
+
|
|
113
|
+
const hasBinary = release.assets && release.assets.some(asset =>
|
|
114
|
+
asset.name === expectedAsset || asset.name.includes(expectedAssetPattern)
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (hasBinary) {
|
|
118
|
+
return version;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
throw new Error('No release with binary assets found');
|
|
123
|
+
}
|
|
124
|
+
|
|
25
125
|
function getPlatform() {
|
|
26
126
|
const platform = os.platform();
|
|
27
127
|
const arch = os.arch();
|
|
@@ -125,14 +225,10 @@ function extractTarGz(tarGzPath, destDir) {
|
|
|
125
225
|
function extractZip(zipPath, destDir) {
|
|
126
226
|
try {
|
|
127
227
|
if (process.platform === 'win32') {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
} catch {
|
|
133
|
-
exit 1
|
|
134
|
-
}
|
|
135
|
-
`;
|
|
228
|
+
// Use single-line command to avoid PowerShell multi-line parsing issues
|
|
229
|
+
const escapedZipPath = zipPath.replace(/'/g, "''");
|
|
230
|
+
const escapedDestDir = destDir.replace(/'/g, "''");
|
|
231
|
+
const psCommand = `Expand-Archive -Path '${escapedZipPath}' -DestinationPath '${escapedDestDir}' -Force -ErrorAction Stop`;
|
|
136
232
|
execSync(`powershell -NoProfile -ExecutionPolicy Bypass -Command "${psCommand}"`, { stdio: 'pipe' });
|
|
137
233
|
} else {
|
|
138
234
|
try {
|
|
@@ -186,9 +282,43 @@ function findBinary(dir, platform) {
|
|
|
186
282
|
throw new Error(`Binary ${binaryName} not found in archive`);
|
|
187
283
|
}
|
|
188
284
|
|
|
285
|
+
async function downloadAndInstall(version, platform, arch, binaryPath) {
|
|
286
|
+
const archiveName = getArchiveName(version, platform, arch);
|
|
287
|
+
const tagVersion = version.startsWith('v') ? version : `v${version}`;
|
|
288
|
+
const downloadUrl = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/releases/download/${tagVersion}/${archiveName}`;
|
|
289
|
+
const archivePath = path.join(CACHE_DIR, archiveName);
|
|
290
|
+
|
|
291
|
+
console.log(`Installing cc-helper ${version} for ${platform}-${arch}...`);
|
|
292
|
+
|
|
293
|
+
await downloadFile(downloadUrl, archivePath);
|
|
294
|
+
|
|
295
|
+
// Extract archive
|
|
296
|
+
console.log('Extracting archive...');
|
|
297
|
+
const tempDir = path.join(CACHE_DIR, 'temp_extract_' + Date.now());
|
|
298
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
299
|
+
|
|
300
|
+
extractArchive(archivePath, tempDir, platform);
|
|
301
|
+
|
|
302
|
+
// Find and move binary
|
|
303
|
+
const extractedBinaryPath = findBinary(tempDir, platform);
|
|
304
|
+
|
|
305
|
+
if (fs.existsSync(binaryPath)) {
|
|
306
|
+
fs.unlinkSync(binaryPath);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fs.copyFileSync(extractedBinaryPath, binaryPath);
|
|
310
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
311
|
+
|
|
312
|
+
// Clean up
|
|
313
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
314
|
+
fs.unlinkSync(archivePath);
|
|
315
|
+
|
|
316
|
+
// Save version
|
|
317
|
+
fs.writeFileSync(VERSION_FILE, version);
|
|
318
|
+
}
|
|
319
|
+
|
|
189
320
|
async function install() {
|
|
190
321
|
try {
|
|
191
|
-
const packageVersion = getPackageVersion();
|
|
192
322
|
const { platform, arch } = getPlatform();
|
|
193
323
|
const binaryName = getBinaryName(platform);
|
|
194
324
|
const binaryPath = path.join(CACHE_DIR, binaryName);
|
|
@@ -198,49 +328,39 @@ async function install() {
|
|
|
198
328
|
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
199
329
|
}
|
|
200
330
|
|
|
331
|
+
// Determine version to install
|
|
332
|
+
let targetVersion = getPackageVersion();
|
|
333
|
+
|
|
201
334
|
// Check if already exists and up to date
|
|
202
|
-
if (fs.existsSync(binaryPath) && fs.existsSync(VERSION_FILE)) {
|
|
335
|
+
if (targetVersion && fs.existsSync(binaryPath) && fs.existsSync(VERSION_FILE)) {
|
|
203
336
|
const currentVersion = fs.readFileSync(VERSION_FILE, 'utf8').trim();
|
|
204
|
-
if (currentVersion ===
|
|
337
|
+
if (currentVersion === targetVersion) {
|
|
205
338
|
console.log(`cc-helper ${currentVersion} is already installed`);
|
|
206
339
|
process.exit(0);
|
|
207
340
|
}
|
|
208
341
|
}
|
|
209
342
|
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
extractArchive(archivePath, tempDir, platform);
|
|
226
|
-
|
|
227
|
-
// Find and move binary
|
|
228
|
-
const extractedBinaryPath = findBinary(tempDir, platform);
|
|
229
|
-
|
|
230
|
-
if (fs.existsSync(binaryPath)) {
|
|
231
|
-
fs.unlinkSync(binaryPath);
|
|
343
|
+
// Try to download the package version first
|
|
344
|
+
if (targetVersion) {
|
|
345
|
+
try {
|
|
346
|
+
await downloadAndInstall(targetVersion, platform, arch, binaryPath);
|
|
347
|
+
console.log('Installation complete!');
|
|
348
|
+
console.log(`Binary location: ${binaryPath}`);
|
|
349
|
+
return;
|
|
350
|
+
} catch (err) {
|
|
351
|
+
// If download failed (e.g., version not found or no binary), try to find a release with binary
|
|
352
|
+
if (err.message.includes('404')) {
|
|
353
|
+
console.log(`Version ${targetVersion} not available, finding compatible release...`);
|
|
354
|
+
} else {
|
|
355
|
+
throw err;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
232
358
|
}
|
|
233
359
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
239
|
-
fs.unlinkSync(archivePath);
|
|
240
|
-
|
|
241
|
-
// Save version
|
|
242
|
-
fs.writeFileSync(VERSION_FILE, packageVersion);
|
|
243
|
-
|
|
360
|
+
// Find a release that has binary assets for this platform
|
|
361
|
+
console.log('Finding latest compatible release...');
|
|
362
|
+
const compatibleVersion = await findReleaseWithBinary(platform, arch);
|
|
363
|
+
await downloadAndInstall(compatibleVersion, platform, arch, binaryPath);
|
|
244
364
|
console.log('Installation complete!');
|
|
245
365
|
console.log(`Binary location: ${binaryPath}`);
|
|
246
366
|
} catch (err) {
|