openclaw-weiyuan-init 1.0.79 → 1.0.80
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/lib/commands.js +1 -1
- package/lib/downloader.js +104 -73
- package/package.json +1 -1
package/lib/commands.js
CHANGED
|
@@ -342,7 +342,7 @@ async function runInit(options) {
|
|
|
342
342
|
|
|
343
343
|
spinner = ora('下载 weiyuan skill...').start();
|
|
344
344
|
const zipPath = path.join(weiyuanPath, source.zipFilename);
|
|
345
|
-
const downloadSuccess = await downloadFile(source.downloadUrl, zipPath, spinner);
|
|
345
|
+
const downloadSuccess = await downloadFile(source.downloadUrl, zipPath, spinner, source.sha256 || '');
|
|
346
346
|
if (!downloadSuccess) {
|
|
347
347
|
spinner.fail('下载失败');
|
|
348
348
|
throw new Error('下载失败');
|
package/lib/downloader.js
CHANGED
|
@@ -1,73 +1,104 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function trimSlash(url) {
|
|
6
|
+
return String(url || '').replace(/\/+$/, '');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function toZipFilename(downloadUrl) {
|
|
10
|
+
const clean = String(downloadUrl || '').split('?')[0].split('#')[0];
|
|
11
|
+
const parts = clean.split('/');
|
|
12
|
+
return parts[parts.length - 1] || 'weiyuan-openclaw-skill.zip';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function resolvePackageSource({ downloadUrl, upgradeBaseUrl, spinner = null }) {
|
|
16
|
+
if (downloadUrl) {
|
|
17
|
+
return { downloadUrl, zipFilename: toZipFilename(downloadUrl), version: null, sha256: '', from: 'download_url' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const base = trimSlash(upgradeBaseUrl);
|
|
21
|
+
const nonce = Date.now();
|
|
22
|
+
try {
|
|
23
|
+
const latestMetaUrl = `${base}/SKILL_LATEST.json?nocache=${nonce}`;
|
|
24
|
+
const latestMetaResp = await axios.get(latestMetaUrl, { timeout: 10000 });
|
|
25
|
+
const meta = latestMetaResp.data || {};
|
|
26
|
+
const version = String(meta.version || '').trim();
|
|
27
|
+
if (version) {
|
|
28
|
+
const zipFilename = String(meta.zipName || '').trim() || `weiyuan-openclaw-skill-v${version}.zip`;
|
|
29
|
+
const resolvedDownloadUrl = `${base}/${zipFilename}?nocache=${nonce}`;
|
|
30
|
+
const sha256 = String(meta.sha256 || '').trim().toLowerCase();
|
|
31
|
+
if (spinner) {
|
|
32
|
+
spinner.text = `已解析最新版本: v${version}`;
|
|
33
|
+
}
|
|
34
|
+
return { downloadUrl: resolvedDownloadUrl, zipFilename, version, sha256, from: 'latest_json' };
|
|
35
|
+
}
|
|
36
|
+
} catch (_e) {
|
|
37
|
+
// fallback to txt
|
|
38
|
+
}
|
|
39
|
+
const latestUrl = `${base}/LATEST_SKILL_VERSION.txt?nocache=${nonce}`;
|
|
40
|
+
const latestResp = await axios.get(latestUrl, { timeout: 10000 });
|
|
41
|
+
const version = String(latestResp.data || '').trim();
|
|
42
|
+
if (!version) throw new Error('empty_latest_version');
|
|
43
|
+
const zipFilename = `weiyuan-openclaw-skill-v${version}.zip`;
|
|
44
|
+
const resolvedDownloadUrl = `${base}/${zipFilename}?nocache=${nonce}`;
|
|
45
|
+
if (spinner) {
|
|
46
|
+
spinner.text = `已解析最新版本: v${version}`;
|
|
47
|
+
}
|
|
48
|
+
return { downloadUrl: resolvedDownloadUrl, zipFilename, version, sha256: '', from: 'latest_version' };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function downloadFile(url, outputPath, spinner = null, expectedSha256 = '') {
|
|
52
|
+
try {
|
|
53
|
+
const response = await axios({
|
|
54
|
+
method: 'GET',
|
|
55
|
+
url: url,
|
|
56
|
+
responseType: 'stream',
|
|
57
|
+
timeout: 60000,
|
|
58
|
+
maxContentLength: Infinity,
|
|
59
|
+
maxBodyLength: Infinity
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const totalLength = parseInt(response.headers['content-length'], 10);
|
|
63
|
+
let downloadedLength = 0;
|
|
64
|
+
|
|
65
|
+
const writer = fs.createWriteStream(outputPath);
|
|
66
|
+
|
|
67
|
+
response.data.on('data', (chunk) => {
|
|
68
|
+
downloadedLength += chunk.length;
|
|
69
|
+
if (spinner && totalLength) {
|
|
70
|
+
const percent = (downloadedLength / totalLength * 100).toFixed(1);
|
|
71
|
+
spinner.text = `下载中... ${percent}% (${(downloadedLength / 1024 / 1024).toFixed(1)}MB / ${(totalLength / 1024 / 1024).toFixed(1)}MB)`;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
response.data.pipe(writer);
|
|
76
|
+
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
writer.on('finish', async () => {
|
|
79
|
+
try {
|
|
80
|
+
if (expectedSha256) {
|
|
81
|
+
const fileBuffer = await fs.readFile(outputPath);
|
|
82
|
+
const actualSha256 = crypto.createHash('sha256').update(fileBuffer).digest('hex').toLowerCase();
|
|
83
|
+
const expected = String(expectedSha256).toLowerCase();
|
|
84
|
+
if (actualSha256 !== expected) {
|
|
85
|
+
return reject(new Error(`skill_package_hash_mismatch expected=${expected} got=${actualSha256}`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
resolve(true);
|
|
89
|
+
} catch (hashErr) {
|
|
90
|
+
reject(hashErr);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
writer.on('error', reject);
|
|
94
|
+
response.data.on('error', reject);
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (spinner) {
|
|
98
|
+
spinner.text = `下载失败: ${error.message}`;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { downloadFile, resolvePackageSource };
|