openclaw-plugin-yuanbao 2.5.0 → 2.5.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.
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-plugin-yuanbao",
3
3
  "name": "元宝 Bot",
4
4
  "description": "Tencent YuanBao intelligent bot channel plugin",
5
- "version": "2.5.0",
5
+ "version": "2.5.1",
6
6
  "channels": [
7
7
  "yuanbao"
8
8
  ],
@@ -1,29 +1,25 @@
1
- import { execSync } from 'node:child_process';
2
1
  import { dirname } from 'node:path';
2
+ import { runPluginCommandWithTimeout } from 'openclaw/plugin-sdk/matrix';
3
3
  import { resolveDefaultYuanbaoAccountId, resolveYuanbaoAccount } from '../accounts.js';
4
4
  import { createLog } from '../logger.js';
5
5
  const log = createLog('upgrade');
6
- function resolveNpmBin() {
6
+ async function resolveNpmBin() {
7
7
  try {
8
- const result = execSync('which npm', {
9
- encoding: 'utf8',
10
- timeout: 5000,
11
- }).trim();
12
- if (result)
13
- return result;
8
+ const result = await runPluginCommandWithTimeout({ argv: ['which', 'npm'], timeoutMs: 5000, env: makeEnv() });
9
+ const resolved = result.stdout.trim();
10
+ if (result.code === 0 && resolved)
11
+ return resolved;
14
12
  }
15
13
  catch {
16
14
  }
17
15
  return 'npm';
18
16
  }
19
- function resolveOpenclawBin() {
17
+ async function resolveOpenclawBin() {
20
18
  try {
21
- const result = execSync('which openclaw', {
22
- encoding: 'utf8',
23
- timeout: 5000,
24
- }).trim();
25
- if (result)
26
- return result;
19
+ const result = await runPluginCommandWithTimeout({ argv: ['which', 'openclaw'], timeoutMs: 5000, env: makeEnv() });
20
+ const resolved = result.stdout.trim();
21
+ if (result.code === 0 && resolved)
22
+ return resolved;
27
23
  }
28
24
  catch {
29
25
  }
@@ -51,29 +47,41 @@ function compareStableVersions(a, b) {
51
47
  function isStableVersion(version) {
52
48
  return /^\d+\.\d+\.\d+$/.test(version);
53
49
  }
54
- function fetchLatestStableVersion() {
55
- const npmBin = resolveNpmBin();
50
+ async function fetchLatestStableVersion() {
51
+ const npmBin = await resolveNpmBin();
56
52
  log.debug('npm 路径', { npmBin, nodeExecPath: process.execPath });
57
53
  try {
58
- const registry = execSync(`"${npmBin}" config get registry`, {
59
- encoding: 'utf8',
54
+ const regResult = await runPluginCommandWithTimeout({
55
+ argv: [npmBin, 'config', 'get', 'registry'],
56
+ timeoutMs: 5000,
60
57
  env: makeEnv(),
61
- stdio: ['pipe', 'pipe', 'pipe'],
62
- timeout: 5000,
63
- }).trim();
64
- log.debug('当前 npm registry', { registry });
58
+ });
59
+ if (regResult.code === 0) {
60
+ log.debug('当前 npm registry', { registry: regResult.stdout.trim() });
61
+ }
62
+ else {
63
+ log.debug('无法读取 npm registry 配置');
64
+ }
65
65
  }
66
66
  catch {
67
67
  log.debug('无法读取 npm registry 配置');
68
68
  }
69
69
  log.debug('查询 npm 最新正式版本', { package: PLUGIN_ID });
70
70
  try {
71
- const raw = execSync(`"${npmBin}" view ${PLUGIN_ID} versions --json`, {
72
- encoding: 'utf8',
71
+ const result = await runPluginCommandWithTimeout({
72
+ argv: [npmBin, 'view', PLUGIN_ID, 'versions', '--json'],
73
+ timeoutMs: EXEC_TIMEOUT_MS,
73
74
  env: makeEnv(),
74
- stdio: ['pipe', 'pipe', 'pipe'],
75
- timeout: EXEC_TIMEOUT_MS,
76
75
  });
76
+ if (result.code !== 0) {
77
+ const stderr = result.stderr.trim() || undefined;
78
+ log.error('npm view 执行失败', {
79
+ summary: stderr?.split('\n')[0] ?? `exit code ${result.code}`,
80
+ ...(stderr ? { stderr } : {}),
81
+ });
82
+ return null;
83
+ }
84
+ const raw = result.stdout;
77
85
  log.debug('npm view 输出', { raw });
78
86
  const parsed = JSON.parse(raw.trim());
79
87
  const allVersions = Array.isArray(parsed) ? parsed : [parsed];
@@ -88,26 +96,29 @@ function fetchLatestStableVersion() {
88
96
  return latest;
89
97
  }
90
98
  catch (e) {
91
- const stderr = extractStderr(e);
92
- log.error('npm view 执行失败', {
93
- summary: firstLine(e),
94
- ...(stderr ? { stderr } : {}),
95
- });
99
+ log.error('npm view 执行失败', { summary: firstLine(e) });
96
100
  return null;
97
101
  }
98
102
  }
99
- function readInstalledVersion(pluginId) {
103
+ async function readInstalledVersion(pluginId) {
100
104
  log.debug('读取已安装版本', { pluginId });
101
105
  try {
102
- const openclawBin = resolveOpenclawBin();
106
+ const openclawBin = await resolveOpenclawBin();
103
107
  log.debug('openclaw 路径', { openclawBin });
104
- const output = execSync(`"${openclawBin}" plugins list`, {
105
- encoding: 'utf8',
108
+ const result = await runPluginCommandWithTimeout({
109
+ argv: [openclawBin, 'plugins', 'list'],
110
+ timeoutMs: EXEC_TIMEOUT_MS,
106
111
  env: makeEnv(),
107
- stdio: ['pipe', 'pipe', 'pipe'],
108
- timeout: EXEC_TIMEOUT_MS,
109
112
  });
110
- for (const line of output.split('\n')) {
113
+ if (result.code !== 0) {
114
+ const stderr = result.stderr.trim() || undefined;
115
+ log.warn('openclaw plugins list 执行失败', {
116
+ summary: stderr?.split('\n')[0] ?? `exit code ${result.code}`,
117
+ ...(stderr ? { stderr } : {}),
118
+ });
119
+ return null;
120
+ }
121
+ for (const line of result.stdout.split('\n')) {
111
122
  if (line.toLowerCase().includes(pluginId.toLowerCase())) {
112
123
  const match = line.match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
113
124
  if (match) {
@@ -120,59 +131,51 @@ function readInstalledVersion(pluginId) {
120
131
  return null;
121
132
  }
122
133
  catch (e) {
123
- const stderr = extractStderr(e);
124
- log.warn('openclaw plugins list 执行失败', {
125
- summary: firstLine(e),
126
- ...(stderr ? { stderr } : {}),
127
- });
134
+ log.warn('openclaw plugins list 执行失败', { summary: firstLine(e) });
128
135
  return null;
129
136
  }
130
137
  }
131
- function installPlugin(version) {
132
- const openclawBin = resolveOpenclawBin();
133
- log.info('执行安装命令', { command: `"${openclawBin}" plugins install ${PLUGIN_ID}@${version}` });
138
+ async function installPlugin(version) {
139
+ const openclawBin = await resolveOpenclawBin();
140
+ const argv = [openclawBin, 'plugins', 'install', `${PLUGIN_ID}@${version}`];
141
+ log.info('执行安装命令', { command: argv.join(' ') });
134
142
  try {
135
- execSync(`"${openclawBin}" plugins install ${PLUGIN_ID}@${version}`, {
136
- encoding: 'utf8',
137
- env: makeEnv(),
138
- stdio: ['pipe', 'pipe', 'pipe'],
139
- timeout: EXEC_TIMEOUT_MS,
140
- });
143
+ const result = await runPluginCommandWithTimeout({ argv, timeoutMs: EXEC_TIMEOUT_MS, env: makeEnv() });
144
+ if (result.code !== 0) {
145
+ const stderr = result.stderr.trim() || undefined;
146
+ const summary = stderr?.split('\n')[0] ?? `exit code ${result.code}`;
147
+ log.error('安装命令执行失败', { summary, ...(stderr ? { stderr } : {}) });
148
+ return { ok: false, error: summary };
149
+ }
141
150
  log.info('安装命令执行完毕');
142
151
  return { ok: true };
143
152
  }
144
153
  catch (e) {
145
- const stderr = extractStderr(e);
146
- log.error('安装命令执行失败', {
147
- summary: firstLine(e),
148
- ...(stderr ? { stderr } : {}),
149
- });
154
+ log.error('安装命令执行失败', { summary: firstLine(e) });
150
155
  return { ok: false, error: firstLine(e) };
151
156
  }
152
157
  }
153
- function updatePlugin() {
154
- const openclawBin = resolveOpenclawBin();
155
- log.info('执行更新命令', { command: `"${openclawBin}" plugins update ${PLUGIN_ID}` });
158
+ async function updatePlugin() {
159
+ const openclawBin = await resolveOpenclawBin();
160
+ const argv = [openclawBin, 'plugins', 'update', PLUGIN_ID];
161
+ log.info('执行更新命令', { command: argv.join(' ') });
156
162
  try {
157
- execSync(`"${openclawBin}" plugins update ${PLUGIN_ID}`, {
158
- encoding: 'utf8',
159
- env: makeEnv(),
160
- stdio: ['pipe', 'pipe', 'pipe'],
161
- timeout: EXEC_TIMEOUT_MS,
162
- });
163
+ const result = await runPluginCommandWithTimeout({ argv, timeoutMs: EXEC_TIMEOUT_MS, env: makeEnv() });
164
+ if (result.code !== 0) {
165
+ const stderr = result.stderr.trim() || undefined;
166
+ const summary = stderr?.split('\n')[0] ?? `exit code ${result.code}`;
167
+ log.warn('更新命令执行失败,将尝试 CDN 重装', { summary, ...(stderr ? { stderr } : {}) });
168
+ return { ok: false, error: summary };
169
+ }
163
170
  log.info('更新命令执行完毕');
164
171
  return { ok: true };
165
172
  }
166
173
  catch (e) {
167
- const stderr = extractStderr(e);
168
- log.warn('更新命令执行失败,将尝试 CDN 重装', {
169
- summary: firstLine(e),
170
- ...(stderr ? { stderr } : {}),
171
- });
174
+ log.warn('更新命令执行失败,将尝试 CDN 重装', { summary: firstLine(e) });
172
175
  return { ok: false, error: firstLine(e) };
173
176
  }
174
177
  }
175
- function reinstallViaCdn(params) {
178
+ async function reinstallViaCdn(params) {
176
179
  const { appKey, appSecret, token } = params;
177
180
  const botToken = (appKey && appSecret) ? `${appKey}:${appSecret}` : token;
178
181
  if (!botToken) {
@@ -181,45 +184,45 @@ function reinstallViaCdn(params) {
181
184
  }
182
185
  const tokenSource = (appKey && appSecret) ? 'appKey:appSecret' : 'token';
183
186
  log.info('执行 CDN 重装', { scriptUrl: INSTALL_SCRIPT_URL, tokenSource });
184
- const cmd = [
187
+ const shellCmd = [
185
188
  `bash <(curl -fsSL ${INSTALL_SCRIPT_URL})`,
186
189
  `--bot-token ${shellQuote(botToken)}`,
187
190
  ].join(' ');
188
191
  try {
189
- execSync(cmd, {
190
- encoding: 'utf8',
191
- shell: '/bin/bash',
192
+ const result = await runPluginCommandWithTimeout({
193
+ argv: ['bash', '-c', shellCmd],
194
+ timeoutMs: INSTALL_SCRIPT_TIMEOUT_MS,
192
195
  env: makeEnv(),
193
- stdio: ['pipe', 'pipe', 'pipe'],
194
- timeout: INSTALL_SCRIPT_TIMEOUT_MS,
195
196
  });
197
+ if (result.code !== 0) {
198
+ const stderr = result.stderr.trim() || undefined;
199
+ const summary = stderr?.split('\n')[0] ?? `exit code ${result.code}`;
200
+ log.error('CDN 重装脚本执行失败', { summary, ...(stderr ? { stderr } : {}) });
201
+ return { ok: false, error: summary };
202
+ }
196
203
  log.info('CDN 重装脚本执行完毕');
197
204
  return { ok: true };
198
205
  }
199
206
  catch (e) {
200
- const stderr = extractStderr(e);
201
- log.error('CDN 重装脚本执行失败', {
202
- summary: firstLine(e),
203
- ...(stderr ? { stderr } : {}),
204
- });
207
+ log.error('CDN 重装脚本执行失败', { summary: firstLine(e) });
205
208
  return { ok: false, error: firstLine(e) };
206
209
  }
207
210
  }
208
- function performUpgrade(config, accountId) {
211
+ async function performUpgrade(config, accountId) {
209
212
  const lines = [];
210
213
  log.info('开始升级流程');
211
- const latestVersion = fetchLatestStableVersion();
214
+ const latestVersion = await fetchLatestStableVersion();
212
215
  if (!latestVersion) {
213
216
  log.error('无法获取最新版本,升级流程中止');
214
217
  return '❌ 无法从 npm 获取最新版本信息,请检查网络连接或稍后重试。';
215
218
  }
216
- const currentVersion = readInstalledVersion(PLUGIN_ID);
219
+ const currentVersion = await readInstalledVersion(PLUGIN_ID);
217
220
  log.info('版本对比', { currentVersion: currentVersion ?? '(未安装)', latestVersion });
218
221
  if (!currentVersion) {
219
222
  log.info('插件未安装,执行首次安装', { targetVersion: latestVersion });
220
223
  lines.push(`📦 未检测到已安装版本,正在安装 v${latestVersion}...`);
221
- const installResult = installPlugin(latestVersion);
222
- const installedVersion = readInstalledVersion(PLUGIN_ID);
224
+ const installResult = await installPlugin(latestVersion);
225
+ const installedVersion = await readInstalledVersion(PLUGIN_ID);
223
226
  log.info('安装后版本检查', { installedVersion: installedVersion ?? '(未检测到)', expected: latestVersion });
224
227
  if (installedVersion === latestVersion) {
225
228
  log.info('安装成功', { version: installedVersion });
@@ -241,8 +244,8 @@ function performUpgrade(config, accountId) {
241
244
  }
242
245
  log.info('发现可用更新,开始升级', { from: currentVersion, to: latestVersion });
243
246
  lines.push(`🔄 当前版本 v${currentVersion} → 目标版本 v${latestVersion},开始升级...`);
244
- updatePlugin();
245
- const afterUpdateVersion = readInstalledVersion(PLUGIN_ID);
247
+ await updatePlugin();
248
+ const afterUpdateVersion = await readInstalledVersion(PLUGIN_ID);
246
249
  log.info('常规升级后版本检查', { afterUpdateVersion: afterUpdateVersion ?? '(未检测到)', expected: latestVersion });
247
250
  if (afterUpdateVersion === latestVersion) {
248
251
  log.info('常规升级成功', { from: currentVersion, to: afterUpdateVersion });
@@ -262,8 +265,8 @@ function performUpgrade(config, accountId) {
262
265
  });
263
266
  const { appKey, appSecret, token } = resolvedAccount;
264
267
  lines.push('执行openclaw plugins update失败,尝试重新安装最新版本,预计需要花费 1-2 分钟,请耐心等待');
265
- reinstallViaCdn({ appKey, appSecret, token });
266
- const finalVersion = readInstalledVersion(PLUGIN_ID);
268
+ await reinstallViaCdn({ appKey, appSecret, token });
269
+ const finalVersion = await readInstalledVersion(PLUGIN_ID);
267
270
  log.info('CDN 重装后版本检查', { finalVersion: finalVersion ?? '(未检测到)', expected: latestVersion });
268
271
  if (finalVersion === latestVersion) {
269
272
  log.info('CDN 重装升级成功', { from: currentVersion, to: finalVersion });
@@ -281,8 +284,8 @@ function makeUpgradeCommand(name, description) {
281
284
  name,
282
285
  description,
283
286
  requireAuth: false,
284
- handler: (ctx) => {
285
- const text = performUpgrade(ctx.config, ctx.accountId);
287
+ handler: async (ctx) => {
288
+ const text = await performUpgrade(ctx.config, ctx.accountId);
286
289
  return { text };
287
290
  },
288
291
  };
@@ -294,23 +297,7 @@ function shellQuote(value) {
294
297
  }
295
298
  function firstLine(e) {
296
299
  if (e instanceof Error) {
297
- const { stderr } = e;
298
- if (stderr) {
299
- const stderrStr = Buffer.isBuffer(stderr) ? stderr.toString('utf8') : String(stderr);
300
- const trimmed = stderrStr.trim();
301
- if (trimmed)
302
- return trimmed.split('\n')[0];
303
- }
304
300
  return e.message.split('\n')[0] ?? String(e);
305
301
  }
306
302
  return String(e).split('\n')[0];
307
303
  }
308
- function extractStderr(e) {
309
- if (!(e instanceof Error))
310
- return undefined;
311
- const { stderr } = e;
312
- if (!stderr)
313
- return undefined;
314
- const s = Buffer.isBuffer(stderr) ? stderr.toString('utf8') : String(stderr);
315
- return s.trim() || undefined;
316
- }
@@ -5,9 +5,7 @@ import { apiGetUploadInfo } from '../../yuanbao-server/api.js';
5
5
  import { createLog } from '../../logger.js';
6
6
  const DEFAULT_RECORD_API_URL = 'https://yuanbao.tencent.com/e/api/clawLogUpload';
7
7
  function resolveRecordApiUrl(config) {
8
- const apiUrl = config?.logUploadApiUrl?.trim()
9
- || process.env.logUploadApiUrl?.trim()
10
- || process.env.YUANBAO_LOG_UPLOAD_API_URL?.trim() || DEFAULT_RECORD_API_URL;
8
+ const apiUrl = config?.logUploadApiUrl?.trim() || DEFAULT_RECORD_API_URL;
11
9
  if (!apiUrl) {
12
10
  throw new Error('缺少 logUploadApiUrl 配置或环境变量 YUANBAO_LOG_UPLOAD_API_URL');
13
11
  }
@@ -1,30 +1,30 @@
1
- import { execSync } from 'node:child_process';
2
1
  import { readFile, readdir, stat } from 'node:fs/promises';
2
+ import { runPluginCommandWithTimeout } from 'openclaw/plugin-sdk/matrix';
3
3
  import { createLog } from '../../logger.js';
4
4
  import { getYuanbaoRuntime } from '../../runtime.js';
5
5
  const log = createLog('log-upload');
6
6
  const EXEC_TIMEOUT_MS = 10_000;
7
7
  const FILTER_FETCH_LIMIT = 5000;
8
- function resolveOpenclawBin() {
8
+ async function resolveOpenclawBin() {
9
9
  try {
10
- const result = execSync('which openclaw', {
11
- encoding: 'utf8',
12
- timeout: 3000,
13
- }).trim();
14
- if (result)
15
- return result;
10
+ const result = await runPluginCommandWithTimeout({ argv: ['which', 'openclaw'], timeoutMs: 3000 });
11
+ const resolved = result.stdout.trim();
12
+ if (result.code === 0 && resolved)
13
+ return resolved;
16
14
  }
17
15
  catch {
18
16
  }
19
17
  return 'openclaw';
20
18
  }
21
- function readConfigValue(openclawBin, key) {
19
+ async function readConfigValue(openclawBin, key) {
22
20
  try {
23
- const raw = execSync(`"${openclawBin}" config get ${key}`, {
24
- encoding: 'utf8',
25
- stdio: ['pipe', 'pipe', 'pipe'],
26
- timeout: EXEC_TIMEOUT_MS,
27
- }).trim();
21
+ const result = await runPluginCommandWithTimeout({
22
+ argv: [openclawBin, 'config', 'get', key],
23
+ timeoutMs: EXEC_TIMEOUT_MS,
24
+ });
25
+ if (result.code !== 0)
26
+ return undefined;
27
+ const raw = result.stdout.trim();
28
28
  return raw || undefined;
29
29
  }
30
30
  catch {
@@ -67,10 +67,10 @@ function buildTodayLogPath() {
67
67
  return `/tmp/openclaw/openclaw-${date}.log`;
68
68
  }
69
69
  async function readOpenclawLoggingFileFromConfig() {
70
- const openclawBin = resolveOpenclawBin();
70
+ const openclawBin = await resolveOpenclawBin();
71
71
  const configKeys = ['logging.file', 'gateway.logging.file', 'logs.file'];
72
72
  for (const key of configKeys) {
73
- const value = readConfigValue(openclawBin, key);
73
+ const value = await readConfigValue(openclawBin, key);
74
74
  if (value)
75
75
  return value;
76
76
  }
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-plugin-yuanbao",
3
3
  "name": "元宝 Bot",
4
4
  "description": "Tencent YuanBao intelligent bot channel plugin",
5
- "version": "2.5.0",
5
+ "version": "2.5.1",
6
6
  "channels": [
7
7
  "yuanbao"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-plugin-yuanbao",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "type": "module",
5
5
  "description": "Tencent YuanBao intelligent bot channel plugin",
6
6
  "license": "MIT",