openclaw-weiyuan-init 1.0.80 → 1.0.82

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 CHANGED
@@ -53,7 +53,7 @@ const DEFAULT_SKILL_TSCONFIG = {
53
53
  };
54
54
 
55
55
  const DEFAULT_FIXED_MESSAGES = {
56
- sceneA: "【微元协作】✨ 已为你接入微元系统~\n让协作自然发生,Let collaboration happen naturally 💫\n💡 对话框输入「查看帮助」即可查看使用指南",
56
+ sceneA: "【微元协作】✨ 已为你接入微元系统~\n让协作自然发生,Let collaboration happen naturally 💫",
57
57
  sceneB: "【微元协作】✨ 已为你接入微元系统~ 并已加入对应项目~\n项目ID:{projectId}\n\n让协作自然发生,Let collaboration happen naturally 💫",
58
58
  sceneC: "【微元协作】✨ 已加入对应项目~\n项目ID:{projectId}\n\n让协作自然发生,Let collaboration happen naturally 💫"
59
59
  };
@@ -86,6 +86,52 @@ function renderFixedMessage(template, vars = {}) {
86
86
  return String(template).replace(/\{(\w+)\}/g, (_, k) => (vars[k] !== undefined ? String(vars[k]) : ""));
87
87
  }
88
88
 
89
+ function printReleaseNotes(notes) {
90
+ if (!notes) return;
91
+ const lines = Array.isArray(notes.changes) ? notes.changes.filter(Boolean).slice(0, 8) : [];
92
+ if (!notes.summary && lines.length === 0) return;
93
+ const versionText = notes.version ? `v${notes.version}` : "最新版本";
94
+ console.log(chalk.cyan(`\n📦 ${versionText} 变更说明`));
95
+ if (notes.summary) console.log(chalk.white(`- ${notes.summary}`));
96
+ for (const item of lines) {
97
+ if (notes.summary && item === notes.summary) continue;
98
+ console.log(chalk.gray(`- ${item}`));
99
+ }
100
+ }
101
+
102
+ async function persistReleaseNotes(weiyuanPath, notes) {
103
+ const notesDir = path.join(weiyuanPath, 'release-notes');
104
+ const latestPath = path.join(notesDir, 'latest.json');
105
+ const ttlDays = 30;
106
+ const now = Date.now();
107
+ const expiresAt = new Date(now + ttlDays * 24 * 60 * 60 * 1000).toISOString();
108
+ await fs.ensureDir(notesDir);
109
+ if (notes && typeof notes === 'object') {
110
+ const payload = {
111
+ ...notes,
112
+ fetchedAt: new Date(now).toISOString(),
113
+ expiresAt
114
+ };
115
+ await fs.writeJson(latestPath, payload, { spaces: 2 });
116
+ } else if (await fs.pathExists(latestPath)) {
117
+ try {
118
+ const existing = await fs.readJson(latestPath);
119
+ const exp = Date.parse(String(existing && existing.expiresAt ? existing.expiresAt : ''));
120
+ if (!Number.isFinite(exp) || exp <= now) {
121
+ await fs.remove(latestPath);
122
+ }
123
+ } catch (_) {
124
+ await fs.remove(latestPath);
125
+ }
126
+ }
127
+ const files = await fs.readdir(notesDir);
128
+ for (const name of files) {
129
+ if (name === 'latest.json') continue;
130
+ if (!name.toLowerCase().endsWith('.json')) continue;
131
+ await fs.remove(path.join(notesDir, name));
132
+ }
133
+ }
134
+
89
135
  function resolveWorkspacePath(workspaceOption) {
90
136
  const raw = (workspaceOption || DEFAULT_CONFIG.workspaceName).trim();
91
137
  const target = path.isAbsolute(raw) ? raw : path.join(process.cwd(), raw);
@@ -339,6 +385,7 @@ async function runInit(options) {
339
385
  spinner
340
386
  });
341
387
  spinner.succeed(`下载源: ${source.downloadUrl}`);
388
+ printReleaseNotes(source.releaseNotes);
342
389
 
343
390
  spinner = ora('下载 weiyuan skill...').start();
344
391
  const zipPath = path.join(weiyuanPath, source.zipFilename);
@@ -362,6 +409,7 @@ async function runInit(options) {
362
409
  spinner = ora('清理临时文件...').start();
363
410
  try {
364
411
  await fs.remove(zipPath);
412
+ await persistReleaseNotes(weiyuanPath, source.releaseNotes || null);
365
413
  spinner.succeed('清理完成');
366
414
  } catch (error) {
367
415
  spinner.warn('清理失败,可手动删除');
package/lib/downloader.js CHANGED
@@ -12,13 +12,34 @@ function toZipFilename(downloadUrl) {
12
12
  return parts[parts.length - 1] || 'weiyuan-openclaw-skill.zip';
13
13
  }
14
14
 
15
+ async function fetchLatestReleaseNotes(base, nonce) {
16
+ const notesUrl = `${base}/RELEASE_NOTES_LATEST.json?nocache=${nonce}`;
17
+ try {
18
+ const resp = await axios.get(notesUrl, { timeout: 8000 });
19
+ const payload = resp.data || {};
20
+ const version = String(payload.version || '').trim();
21
+ const summary = String(payload.summary || '').trim();
22
+ const changes = Array.isArray(payload.changes) ? payload.changes.map((x) => String(x || '').trim()).filter(Boolean) : [];
23
+ if (!version && !summary && changes.length === 0) return null;
24
+ return {
25
+ version,
26
+ summary,
27
+ changes,
28
+ releasedAt: String(payload.releasedAt || '').trim()
29
+ };
30
+ } catch (_e) {
31
+ return null;
32
+ }
33
+ }
34
+
15
35
  async function resolvePackageSource({ downloadUrl, upgradeBaseUrl, spinner = null }) {
16
36
  if (downloadUrl) {
17
- return { downloadUrl, zipFilename: toZipFilename(downloadUrl), version: null, sha256: '', from: 'download_url' };
37
+ return { downloadUrl, zipFilename: toZipFilename(downloadUrl), version: null, sha256: '', from: 'download_url', releaseNotes: null };
18
38
  }
19
39
 
20
40
  const base = trimSlash(upgradeBaseUrl);
21
41
  const nonce = Date.now();
42
+ const releaseNotes = await fetchLatestReleaseNotes(base, nonce);
22
43
  try {
23
44
  const latestMetaUrl = `${base}/SKILL_LATEST.json?nocache=${nonce}`;
24
45
  const latestMetaResp = await axios.get(latestMetaUrl, { timeout: 10000 });
@@ -31,7 +52,7 @@ async function resolvePackageSource({ downloadUrl, upgradeBaseUrl, spinner = nul
31
52
  if (spinner) {
32
53
  spinner.text = `已解析最新版本: v${version}`;
33
54
  }
34
- return { downloadUrl: resolvedDownloadUrl, zipFilename, version, sha256, from: 'latest_json' };
55
+ return { downloadUrl: resolvedDownloadUrl, zipFilename, version, sha256, from: 'latest_json', releaseNotes };
35
56
  }
36
57
  } catch (_e) {
37
58
  // fallback to txt
@@ -45,7 +66,7 @@ async function resolvePackageSource({ downloadUrl, upgradeBaseUrl, spinner = nul
45
66
  if (spinner) {
46
67
  spinner.text = `已解析最新版本: v${version}`;
47
68
  }
48
- return { downloadUrl: resolvedDownloadUrl, zipFilename, version, sha256: '', from: 'latest_version' };
69
+ return { downloadUrl: resolvedDownloadUrl, zipFilename, version, sha256: '', from: 'latest_version', releaseNotes };
49
70
  }
50
71
 
51
72
  async function downloadFile(url, outputPath, spinner = null, expectedSha256 = '') {
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
- {
2
- "name": "openclaw-weiyuan-init",
3
- "version": "1.0.80",
4
- "description": "OpenClaw Weiyuan Skill 一键初始化工具",
5
- "main": "bin/cli.js",
6
- "bin": {
7
- "openclaw-weiyuan-init": "bin/cli.js"
8
- },
9
- "scripts": {
10
- "start": "node bin/cli.js init"
11
- },
12
- "files": [
13
- "bin/",
14
- "lib/",
15
- "README.md"
16
- ],
17
- "dependencies": {
18
- "axios": "^1.6.0",
19
- "adm-zip": "^0.5.10",
20
- "chalk": "^4.1.2",
21
- "commander": "^11.1.0",
22
- "fs-extra": "^11.1.1",
23
- "ora": "^5.4.1",
24
- "tweetnacl": "^1.0.3"
25
- },
26
- "publishConfig": {
27
- "access": "public"
28
- }
29
- }
1
+ {
2
+ "name": "openclaw-weiyuan-init",
3
+ "version": "1.0.82",
4
+ "description": "OpenClaw Weiyuan Skill 一键初始化工具",
5
+ "main": "bin/cli.js",
6
+ "bin": {
7
+ "openclaw-weiyuan-init": "bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/cli.js init"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "lib/",
15
+ "README.md"
16
+ ],
17
+ "dependencies": {
18
+ "axios": "^1.6.0",
19
+ "adm-zip": "^0.5.10",
20
+ "chalk": "^4.1.2",
21
+ "commander": "^11.1.0",
22
+ "fs-extra": "^11.1.1",
23
+ "ora": "^5.4.1",
24
+ "tweetnacl": "^1.0.3"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }