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.
|
@@ -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 =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
59
|
-
|
|
54
|
+
const regResult = await runPluginCommandWithTimeout({
|
|
55
|
+
argv: [npmBin, 'config', 'get', 'registry'],
|
|
56
|
+
timeoutMs: 5000,
|
|
60
57
|
env: makeEnv(),
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
72
|
-
|
|
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
|
-
|
|
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
|
|
105
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
|
187
|
+
const shellCmd = [
|
|
185
188
|
`bash <(curl -fsSL ${INSTALL_SCRIPT_URL})`,
|
|
186
189
|
`--bot-token ${shellQuote(botToken)}`,
|
|
187
190
|
].join(' ');
|
|
188
191
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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 =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
}
|
package/openclaw.plugin.json
CHANGED