@tencent-weixin/openclaw-weixin-cli 2.0.1 → 2.1.1

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/README.md CHANGED
@@ -19,10 +19,10 @@ npx -y @tencent-weixin/openclaw-weixin-cli install
19
19
 
20
20
  ## 兼容矩阵
21
21
 
22
- | openclaw-weixin | 支持的 OpenClaw | dist-tag | 说明 |
23
- |-----------------|---------------------|----------|------------|
24
- | 1.0.x | >=2026.3.0 <2026.3.22 | `legacy` | 旧宿主维护线 |
25
- | 2.0.x | >=2026.3.22 | `latest` | 新宿主线 |
22
+ | openclaw-weixin | 支持的 OpenClaw | dist-tag | 说明 |
23
+ |-----------------|-----------------------|---------------------------------------|----------------------|
24
+ | 1.0.x | >=2026.3.0 <2026.3.22 | `compat-host-gte2026.3.0-lt2026.3.22` | 兼容轨道 |
25
+ | 2.0.x | >=2026.3.22 | `latest` | 当前推荐主线 |
26
26
 
27
27
  > 从 2.0.0 开始,插件采用独立 semver 版本号,不再对齐宿主 OpenClaw 版本号。
28
28
 
@@ -34,11 +34,11 @@ npx -y @tencent-weixin/openclaw-weixin-cli install
34
34
  # 查看当前 OpenClaw 版本
35
35
  openclaw --version
36
36
 
37
- # 新宿主 (>=2026.3.22):安装 latest 线
37
+ # 当前推荐主线 (>=2026.3.22)
38
38
  openclaw plugins install @tencent/openclaw-weixin@latest
39
39
 
40
- # 旧宿主 (<2026.3.22):安装 legacy 线
41
- openclaw plugins install @tencent/openclaw-weixin@legacy
40
+ # 兼容轨道 (<2026.3.22)
41
+ openclaw plugins install @tencent/openclaw-weixin@compat-host-gte2026.3.0-lt2026.3.22
42
42
  ```
43
43
 
44
44
  ## 运行时版本校验
@@ -49,7 +49,7 @@ openclaw plugins install @tencent/openclaw-weixin@legacy
49
49
  [openclaw-weixin] 宿主版本不兼容!
50
50
  当前 OpenClaw 版本: 2026.3.10
51
51
  当前插件支持范围: >=2026.3.22
52
- 请安装 openclaw-weixin@legacy (1.0.x (旧宿主线))
52
+ 请安装 openclaw-weixin@compat-host-gte2026.3.0-lt2026.3.22
53
53
  或运行: npx @tencent-weixin/openclaw-weixin-cli install (自动选择兼容版本)
54
54
  ```
55
55
 
@@ -68,46 +68,122 @@ openclaw --version
68
68
  npx -y @tencent-weixin/openclaw-weixin-cli install
69
69
  ```
70
70
 
71
- ### 如何安装 legacy 线
71
+ ### 如何查看当前 openclaw 版本
72
72
 
73
73
  ```bash
74
- # 方式一:统一安装器(自动判断)
75
- npx -y @tencent-weixin/openclaw-weixin-cli install
74
+ openclaw --version
75
+ ```
76
+
77
+ ## 发布策略
78
+
79
+ ### 本仓库(CLI installer)与插件仓库的关系
80
+
81
+ 本仓库发布的是 **CLI 安装器**(`openclaw-weixin-cli`),不是插件本身(`openclaw-weixin`)。
82
+
83
+ - **插件仓库**:openclaw-weixin,通过各分支发布到不同的 npm dist-tag
84
+ - **本仓库**:CLI 安装器,根据 `COMPAT_MATRIX` 自动选择正确的插件 dist-tag 来安装
85
+
86
+ ### 分支管理规则
76
87
 
77
- # 方式二:手动指定 dist-tag
78
- openclaw plugins install @tencent/openclaw-weixin@legacy
88
+ 内网和外网通过不同分支管理,prerelease 分支从主分支拉出,合并回主分支:
89
+
90
+ | 分支 | 用途 | package.json 状态 |
91
+ |------|------|------------------|
92
+ | `master-prerelease` | 日常开发 + 内网发布 | 内网发布后保持内网包名和版本 |
93
+ | `master` | 外网发布 | 外网发布后保持外网包名和版本 |
94
+
95
+ **核心原则:**
96
+ - 日常开发和内网发布在 `-prerelease` 分支上进行
97
+ - 需要发外网时,将 prerelease 分支合入对应主分支,再在主分支上发布
98
+ - 发布后 package.json **不回退**,分支保持对应环境的最新发布状态
99
+ - prerelease 和主分支同构(共用 `publish.config.json`),合并时自然更新
100
+
101
+ ### CLI 发布命令
102
+
103
+ ```bash
104
+ # 在 prerelease 分支上发布内网
105
+ node scripts/publish.mjs internal patch
106
+
107
+ # dry run 预检
108
+ node scripts/publish.mjs internal patch --dry-run
79
109
  ```
80
110
 
81
- ### 如何查看当前 openclaw 版本
111
+ 发布脚本会:
112
+ 1. 检查工作区是否干净
113
+ 2. 将 `package.json` 的 name/version 和 `cli.mjs` 的 PLUGIN_SPEC 替换为目标环境的值
114
+ 3. 更新 `publish.config.json` 中对应目标的版本号
115
+ 4. 提交 release commit 并打 git tag
116
+ 5. npm publish
117
+ 6. push commit + tag
118
+
119
+ 发布后 package.json 保持发布态,不回退。如果 npm publish 失败,自动回滚 release commit 和 tag。
120
+
121
+ ### 内网验证后发外网
82
122
 
83
123
  ```bash
84
- openclaw --version
124
+ # ① 在 prerelease 分支上完成内网发布和验证
125
+
126
+ # ② 合入主分支
127
+ git checkout master
128
+ git merge master-prerelease
129
+
130
+ # ③ 在主分支上发布外网
131
+ node scripts/publish.mjs external patch
85
132
  ```
86
133
 
87
- ## 发布策略
134
+ ### 兼容轨道发版流程
88
135
 
89
- ### dist-tag 说明
136
+ openclaw 发布破坏兼容性的新版本时:
90
137
 
91
- | dist-tag | 对应版本线 | 说明 |
92
- |----------|-----------|------|
93
- | `latest` | 2.x | 新宿主线,`npm install` / `openclaw plugins install` 默认安装 |
94
- | `legacy` | 1.x | 旧宿主维护线,需显式指定 `@legacy` |
138
+ #### 1. 插件仓库操作(openclaw-weixin)
95
139
 
96
- ### 发布命令
140
+ ```bash
141
+ # 从当前分支拉出兼容轨道
142
+ git checkout -b compat-host-gteX.Y.Z-ltA.B.C-prerelease master-prerelease
143
+ git checkout -b compat-host-gteX.Y.Z-ltA.B.C master
144
+ # 修改 compat 分支上的 publish.config.json,将 npmTag 改为轨道名
145
+
146
+ # master-prerelease 上适配新宿主,major + 1
147
+ ```
148
+
149
+ #### 2. CLI 仓库操作(本仓库)
97
150
 
98
151
  ```bash
99
- # 发布新宿主线 (2.x)
100
- npm run publish:external:latest -- patch
101
- npm run publish:internal:latest -- patch
152
+ # 更新 lib/compat.mjs 的 COMPAT_MATRIX
102
153
 
103
- # 发布旧宿主维护线 (1.x)
104
- npm run publish:external:legacy -- patch
105
- npm run publish:internal:legacy -- patch
154
+ # 在 prerelease 分支上发布内网验证
155
+ node scripts/publish.mjs internal minor
106
156
 
107
- # dry run 预检
108
- npm run publish:external:latest -- patch --dry-run
157
+ # 验证后合入主分支发外网
158
+ git checkout master && git merge master-prerelease
159
+ node scripts/publish.mjs external minor
109
160
  ```
110
161
 
162
+ #### 3. 验证清单
163
+
164
+ - [ ] `npx @tencent/openclaw-weixin-cli install` 在旧宿主上安装兼容轨道版本
165
+ - [ ] `npx @tencent/openclaw-weixin-cli install` 在新宿主上安装 latest 版本
166
+ - [ ] 已安装旧 tag 的用户重跑 CLI 后自动卸载旧版、安装新版
167
+ - [ ] 插件运行时 `version-check.mjs` 报错信息指引正确
168
+
169
+ ### 兼容轨道 bug 修复
170
+
171
+ ```bash
172
+ # ① 在 compat prerelease 分支上修复
173
+ git checkout compat-host-gteX.Y.Z-ltA.B.C-prerelease
174
+ # cherry-pick 或直接修复
175
+ node scripts/publish.mjs internal patch
176
+
177
+ # ② 需要发外网时合入 compat 主分支
178
+ git checkout compat-host-gteX.Y.Z-ltA.B.C
179
+ git merge compat-host-gteX.Y.Z-ltA.B.C-prerelease
180
+ node scripts/publish.mjs external patch
181
+
182
+ # ③ 评估是否需要同步到 master 和其他 compat 分支
183
+ ```
184
+
185
+ > CLI 不需要重新发布——同一 dist-tag 下的 patch 更新,用户通过 `openclaw plugins update` 即可获取。
186
+
111
187
  ## 开发
112
188
 
113
189
  ```bash
package/cli.mjs CHANGED
@@ -6,12 +6,20 @@ import path from "node:path";
6
6
  import os from "node:os";
7
7
  import {
8
8
  COMPAT_MATRIX,
9
+ compareVersions,
9
10
  findCompatEntry,
10
11
  formatRange,
11
12
  } from "./lib/compat.mjs";
12
13
 
13
14
  const PLUGIN_SPEC = "@tencent-weixin/openclaw-weixin";
14
15
  const CHANNEL_ID = "openclaw-weixin";
16
+ /** Only OpenClaw 2026.3.22–2026.3.23 need node_modules/openclaw symlink for jiti. */
17
+ const SYMLINK_OPENCLAW_MIN = "2026.3.22";
18
+ const SYMLINK_OPENCLAW_MAX = "2026.3.23";
19
+
20
+ const LEGACY_TAG_ALIASES = {
21
+ legacy: "compat-host-gte2026.3.0-lt2026.3.22",
22
+ };
15
23
 
16
24
  // ── helpers ──────────────────────────────────────────────────────────────────
17
25
 
@@ -110,7 +118,23 @@ function resolvePluginExtDir() {
110
118
  * openclaw package root. This lets jiti resolve openclaw/plugin-sdk/*
111
119
  * without openclaw being a runtime dependency.
112
120
  */
113
- function ensureOpenclawSymlink() {
121
+ function hostVersionNeedsOpenclawSymlink(hostVersion) {
122
+ if (!hostVersion) return false;
123
+ const geMin = compareVersions(hostVersion, SYMLINK_OPENCLAW_MIN);
124
+ const leMax = compareVersions(hostVersion, SYMLINK_OPENCLAW_MAX);
125
+ return (
126
+ !Number.isNaN(geMin) &&
127
+ !Number.isNaN(leMax) &&
128
+ geMin >= 0 &&
129
+ leMax <= 0
130
+ );
131
+ }
132
+
133
+ function ensureOpenclawSymlink(hostVersion) {
134
+ if (!hostVersionNeedsOpenclawSymlink(hostVersion)) {
135
+ return;
136
+ }
137
+
114
138
  const hostRoot = resolveHostOpenclawRoot();
115
139
  if (!hostRoot) {
116
140
  error("无法定位宿主 openclaw 包根目录,跳过 symlink 创建");
@@ -130,7 +154,6 @@ function ensureOpenclawSymlink() {
130
154
  try {
131
155
  const existing = fs.readlinkSync(linkPath);
132
156
  if (fs.realpathSync(existing) === fs.realpathSync(hostRoot)) {
133
- log("openclaw symlink 已存在且正确");
134
157
  return;
135
158
  }
136
159
  // Wrong target — remove and recreate
@@ -144,6 +167,30 @@ function ensureOpenclawSymlink() {
144
167
  log(`已创建 symlink: node_modules/openclaw → ${hostRoot}`);
145
168
  }
146
169
 
170
+ // ── installed plugin detection ───────────────────────────────────────────────
171
+
172
+ /**
173
+ * Read the openclaw config and return the dist-tag that was used to install
174
+ * the plugin. OpenClaw stores `plugins.installs.<id>.spec` as the raw
175
+ * install spec (e.g. "@tencent/openclaw-weixin@latest"). We extract the
176
+ * trailing tag/version after the last "@".
177
+ * Returns the tag string, or null if not installed / unreadable.
178
+ */
179
+ function getInstalledPluginTag() {
180
+ const stateDir = process.env.OPENCLAW_STATE_DIR || path.join(os.homedir(), ".openclaw");
181
+ const configPath = path.join(stateDir, "openclaw.json");
182
+ try {
183
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
184
+ const spec = config?.plugins?.installs?.[CHANNEL_ID]?.spec;
185
+ if (!spec) return null;
186
+ const atIdx = spec.lastIndexOf("@");
187
+ const raw = atIdx > 0 ? spec.slice(atIdx + 1) : "latest";
188
+ return LEGACY_TAG_ALIASES[raw] || raw;
189
+ } catch {
190
+ return null;
191
+ }
192
+ }
193
+
147
194
  // ── commands ─────────────────────────────────────────────────────────────────
148
195
 
149
196
  function install() {
@@ -172,7 +219,28 @@ function install() {
172
219
  const pluginInstallSpec = `${PLUGIN_SPEC}@${compat.distTag}`;
173
220
  log(`匹配兼容版本: ${compat.label} (dist-tag: ${compat.distTag})`);
174
221
 
175
- // 3. Install plugin via openclaw
222
+ // 3. If already installed with a different dist-tag, uninstall first
223
+ // so the fresh install picks up the correct dist-tag.
224
+ // If the installed spec is a pinned version number, skip entirely.
225
+ const installedTag = getInstalledPluginTag();
226
+ if (installedTag !== null && /^\d+\.\d+\.\d+/.test(installedTag)) {
227
+ log(`本地已安装插件为固定版本 ${installedTag},跳过升级`);
228
+ return;
229
+ }
230
+ if (installedTag !== null && installedTag !== compat.distTag) {
231
+ log(`本地已安装插件为 @${installedTag},与目标 @${compat.distTag} 不一致,先卸载旧版本...`);
232
+ try {
233
+ run(`echo y | openclaw plugins uninstall "${CHANNEL_ID}"`);
234
+ log("旧版本已卸载");
235
+ } catch (uninstallErr) {
236
+ error("旧版本卸载失败,请手动执行:");
237
+ if (uninstallErr.stderr) console.error(uninstallErr.stderr);
238
+ console.log(` openclaw plugins uninstall "${CHANNEL_ID}"`);
239
+ process.exit(1);
240
+ }
241
+ }
242
+
243
+ // 4. Install plugin via openclaw
176
244
  log(`正在安装插件 ${pluginInstallSpec}...`);
177
245
  try {
178
246
  const installOut = run(`openclaw plugins install "${pluginInstallSpec}"`);
@@ -197,11 +265,11 @@ function install() {
197
265
  }
198
266
  }
199
267
 
200
- // 4. Symlink host openclaw into plugin's node_modules
268
+ // 5. Symlink host openclaw into plugin's node_modules (2026.3.22–2026.3.23 only)
201
269
  // so jiti can resolve openclaw/plugin-sdk/* without a runtime dependency.
202
- ensureOpenclawSymlink();
270
+ ensureOpenclawSymlink(hostVersion);
203
271
 
204
- // 5. Login (interactive QR scan)
272
+ // 6. Login (interactive QR scan)
205
273
  log("插件就绪,开始首次连接...");
206
274
  try {
207
275
  run(`openclaw channels login --channel ${CHANNEL_ID}`, { silent: false });
@@ -211,7 +279,7 @@ function install() {
211
279
  console.log(` openclaw channels login --channel ${CHANNEL_ID}`);
212
280
  }
213
281
 
214
- // 6. Restart gateway so it picks up the new account
282
+ // 7. Restart gateway so it picks up the new account
215
283
  log("正在重启 OpenClaw Gateway...");
216
284
  try {
217
285
  run(`openclaw gateway restart`, { silent: false });
package/lib/compat.mjs CHANGED
@@ -8,15 +8,15 @@
8
8
  export const COMPAT_MATRIX = [
9
9
  {
10
10
  pluginMajor: 1,
11
- distTag: 'legacy',
11
+ distTag: 'compat-host-gte2026.3.0-lt2026.3.22',
12
12
  openclawRange: { gte: '2026.3.0', lt: '2026.3.22' },
13
- label: '1.0.x (旧宿主线)',
13
+ label: '1.0.x (兼容轨道: OpenClaw >=2026.3.0 <2026.3.22)',
14
14
  },
15
15
  {
16
16
  pluginMajor: 2,
17
17
  distTag: 'latest',
18
18
  openclawRange: { gte: '2026.3.22' },
19
- label: '2.0.x (新宿主线)',
19
+ label: '2.0.x (当前最新主线)',
20
20
  },
21
21
  ];
22
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-weixin/openclaw-weixin-cli",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "Lightweight installer for the OpenClaw Weixin channel plugin",
5
5
  "license": "MIT",
6
6
  "author": "Tencent",