rol-websocket-channel 1.7.1 → 1.7.3
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/dist/message-handler.js +0 -1
- package/dist/src/admin/methods/index.js +0 -1
- package/dist/src/admin/methods/system.js +21 -82
- package/message-handler.ts +0 -2
- package/package.json +1 -1
- package/src/admin/methods/index.ts +0 -1
- package/src/admin/methods/system.ts +23 -117
- package/dist/src/admin/methods/system.test.js +0 -166
- package/src/admin/methods/system.test.ts +0 -233
- package/test/custom-message-update-ack.test.ts +0 -77
package/dist/message-handler.js
CHANGED
|
@@ -20,7 +20,6 @@ const methods = new Map([
|
|
|
20
20
|
['system.stop', stop],
|
|
21
21
|
['system.doctorFix', doctorFix],
|
|
22
22
|
['system.logs', logs],
|
|
23
|
-
// OpenClaw core updates are managed outside this plugin.
|
|
24
23
|
['system.pluginSelfUpdate', pluginSelfUpdate],
|
|
25
24
|
['system.currentVersion', currentVersion],
|
|
26
25
|
// Agents
|
|
@@ -8,7 +8,6 @@ const execAsync = promisify(exec);
|
|
|
8
8
|
const execFileAsync = promisify(execFile);
|
|
9
9
|
const UPDATE_COMMAND_TIMEOUT_MS = 10 * 60 * 1000;
|
|
10
10
|
const UPDATE_COMMAND_MAX_BUFFER = 10 * 1024 * 1024;
|
|
11
|
-
const OPENCLAW_UPDATE_TARGET_VERSION = '2026.5.6';
|
|
12
11
|
const CHANNEL_FALLBACK_VERSION = '1.5.9';
|
|
13
12
|
export const ping = async () => {
|
|
14
13
|
return {
|
|
@@ -74,17 +73,21 @@ export const doctorFix = async (_params, context) => {
|
|
|
74
73
|
};
|
|
75
74
|
}
|
|
76
75
|
};
|
|
77
|
-
export const openclawUpdate = async () => {
|
|
78
|
-
// OpenClaw core updates are intentionally not exposed by this plugin.
|
|
79
|
-
return {
|
|
80
|
-
ok: false,
|
|
81
|
-
action: 'openclawUpdate',
|
|
82
|
-
disabled: true,
|
|
83
|
-
restartRecommended: false
|
|
84
|
-
};
|
|
85
|
-
};
|
|
86
76
|
export const pluginSelfUpdate = async (_params, context) => {
|
|
87
77
|
const result = await runOpenClawCommand(['plugins', 'update', 'rol-websocket-channel'], context, 'pluginSelfUpdate');
|
|
78
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
79
|
+
if (isPathSourceUpdateSkip(output)) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
action: 'pluginSelfUpdate',
|
|
83
|
+
plugin: 'rol-websocket-channel',
|
|
84
|
+
skipped: true,
|
|
85
|
+
reason: 'path-source-plugin',
|
|
86
|
+
message: 'rol-websocket-channel is installed as a local path/global plugin, so OpenClaw plugins update skipped it.',
|
|
87
|
+
restartRecommended: false,
|
|
88
|
+
...result
|
|
89
|
+
};
|
|
90
|
+
}
|
|
88
91
|
return {
|
|
89
92
|
ok: true,
|
|
90
93
|
action: 'pluginSelfUpdate',
|
|
@@ -94,19 +97,16 @@ export const pluginSelfUpdate = async (_params, context) => {
|
|
|
94
97
|
};
|
|
95
98
|
};
|
|
96
99
|
export const currentVersion = async (_params, context) => {
|
|
97
|
-
const [
|
|
98
|
-
runOpenClawCommand(['--version'], context, 'currentVersion.openclaw'),
|
|
100
|
+
const [channelPackage, registryInfo] = await Promise.all([
|
|
99
101
|
readJsonFile(path.join(context.projectRoot, 'package.json')),
|
|
100
102
|
queryNpmRegistry('rol-websocket-channel', context)
|
|
101
103
|
]);
|
|
102
|
-
const openclawCurrentVersion = parseOpenClawVersion(openclawCurrentResult.stdout);
|
|
103
104
|
const channelCurrentVersion = normalizeVersion(channelPackage.version);
|
|
104
105
|
const channelLatestVersion = registryInfo.version;
|
|
105
106
|
const targets = [
|
|
106
|
-
buildExternalVersionTarget('openclaw', 'openclaw', openclawCurrentVersion),
|
|
107
107
|
buildCurrentVersionTarget('channel', normalizePackageName(channelPackage.name) || 'rol-websocket-channel', channelCurrentVersion, channelLatestVersion)
|
|
108
108
|
];
|
|
109
|
-
const upgradeTargets = targets.filter((t) => t.
|
|
109
|
+
const upgradeTargets = targets.filter((t) => t.status === 'outdated');
|
|
110
110
|
return {
|
|
111
111
|
ok: true,
|
|
112
112
|
action: 'currentVersion',
|
|
@@ -263,38 +263,6 @@ async function runOpenClawCommand(args, context, action) {
|
|
|
263
263
|
});
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
-
async function runSystemCommand(command, args, cwd, context, action) {
|
|
267
|
-
const options = buildExecOptions(cwd, context.openclawRoot);
|
|
268
|
-
console.log(`[system] exec start: action=${action}, command=${command}, args=${JSON.stringify(args)}, cwd=${options.cwd}`);
|
|
269
|
-
try {
|
|
270
|
-
const { stdout, stderr } = await execFileAsync(command, args, {
|
|
271
|
-
...options,
|
|
272
|
-
timeout: UPDATE_COMMAND_TIMEOUT_MS,
|
|
273
|
-
maxBuffer: UPDATE_COMMAND_MAX_BUFFER
|
|
274
|
-
});
|
|
275
|
-
console.log(`[system] exec success: action=${action}, command=${command}, args=${JSON.stringify(args)}, stdoutLength=${stdout.length}, stderrLength=${stderr.length}`);
|
|
276
|
-
return {
|
|
277
|
-
command,
|
|
278
|
-
args,
|
|
279
|
-
cwd: options.cwd,
|
|
280
|
-
stdout,
|
|
281
|
-
stderr
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
catch (err) {
|
|
285
|
-
const stdout = typeof err?.stdout === 'string' ? err.stdout : '';
|
|
286
|
-
const stderr = typeof err?.stderr === 'string' ? err.stderr : '';
|
|
287
|
-
console.error(`[system] exec failed: action=${action}, command=${command}, args=${JSON.stringify(args)}, cwd=${options.cwd}, stdout=${JSON.stringify(stdout)}, stderr=${JSON.stringify(stderr)}`);
|
|
288
|
-
throw new JsonRpcException(JSON_RPC_ERRORS.internalError, `OpenClaw system command failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
289
|
-
action,
|
|
290
|
-
command,
|
|
291
|
-
args,
|
|
292
|
-
cwd: options.cwd,
|
|
293
|
-
stdout,
|
|
294
|
-
stderr
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
266
|
function buildExecOptions(cwd, openclawRoot) {
|
|
299
267
|
const env = { ...process.env };
|
|
300
268
|
const openclawHome = resolveOpenClawHomeForCli(openclawRoot);
|
|
@@ -338,19 +306,15 @@ async function queryNpmRegistry(packageName, context) {
|
|
|
338
306
|
});
|
|
339
307
|
const data = JSON.parse(stdout);
|
|
340
308
|
const version = typeof data.version === 'string' ? data.version : null;
|
|
341
|
-
const requiredOpenclawVersion = data?.openclaw?.build?.openclawVersion ??
|
|
342
|
-
data?.openclaw?.compat?.pluginApi ??
|
|
343
|
-
null;
|
|
344
309
|
if (!version) {
|
|
345
310
|
console.error(`[system] queryNpmRegistry: no version field in npm view output`);
|
|
346
|
-
return { version: CHANNEL_FALLBACK_VERSION
|
|
311
|
+
return { version: CHANNEL_FALLBACK_VERSION };
|
|
347
312
|
}
|
|
348
|
-
return { version
|
|
313
|
+
return { version };
|
|
349
314
|
}
|
|
350
315
|
catch (err) {
|
|
351
316
|
console.error(`[system] queryNpmRegistry failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
352
|
-
|
|
353
|
-
return { version: CHANNEL_FALLBACK_VERSION, requiredOpenclawVersion: OPENCLAW_UPDATE_TARGET_VERSION };
|
|
317
|
+
return { version: CHANNEL_FALLBACK_VERSION };
|
|
354
318
|
}
|
|
355
319
|
}
|
|
356
320
|
function buildCurrentVersionTarget(name, packageName, currentVersion, latestVersion) {
|
|
@@ -363,34 +327,6 @@ function buildCurrentVersionTarget(name, packageName, currentVersion, latestVers
|
|
|
363
327
|
status
|
|
364
328
|
};
|
|
365
329
|
}
|
|
366
|
-
function buildExternalVersionTarget(name, packageName, currentVersion) {
|
|
367
|
-
return {
|
|
368
|
-
name,
|
|
369
|
-
packageName,
|
|
370
|
-
currentVersion,
|
|
371
|
-
latestVersion: 'managed-externally',
|
|
372
|
-
status: 'external'
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
function buildCommandStep(name, result) {
|
|
376
|
-
return {
|
|
377
|
-
name,
|
|
378
|
-
command: result.command,
|
|
379
|
-
args: result.args,
|
|
380
|
-
cwd: result.cwd,
|
|
381
|
-
stdout: result.stdout,
|
|
382
|
-
stderr: result.stderr
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
function parseOpenClawVersion(stdout) {
|
|
386
|
-
const match = stdout.match(/OpenClaw\s+([^\s(]+)/i);
|
|
387
|
-
if (!match) {
|
|
388
|
-
throw new JsonRpcException(JSON_RPC_ERRORS.internalError, 'Unable to parse OpenClaw version', {
|
|
389
|
-
stdout
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
return normalizeVersion(match[1]);
|
|
393
|
-
}
|
|
394
330
|
function normalizePackageName(value) {
|
|
395
331
|
if (typeof value !== 'string' || !value.trim()) {
|
|
396
332
|
return null;
|
|
@@ -403,3 +339,6 @@ function normalizeVersion(value) {
|
|
|
403
339
|
}
|
|
404
340
|
return value.trim().replace(/^v/i, '');
|
|
405
341
|
}
|
|
342
|
+
function isPathSourceUpdateSkip(output) {
|
|
343
|
+
return /Skipping\s+"?rol-websocket-channel"?\s+\(source:\s*path\)/i.test(output);
|
|
344
|
+
}
|
package/message-handler.ts
CHANGED
package/package.json
CHANGED
|
@@ -55,7 +55,6 @@ const methods = new Map<string, MethodHandler>([
|
|
|
55
55
|
['system.stop', stop],
|
|
56
56
|
['system.doctorFix', doctorFix],
|
|
57
57
|
['system.logs', logs],
|
|
58
|
-
// OpenClaw core updates are managed outside this plugin.
|
|
59
58
|
['system.pluginSelfUpdate', pluginSelfUpdate],
|
|
60
59
|
['system.currentVersion', currentVersion],
|
|
61
60
|
|
|
@@ -10,7 +10,6 @@ const execAsync = promisify(exec);
|
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
11
11
|
const UPDATE_COMMAND_TIMEOUT_MS = 10 * 60 * 1000;
|
|
12
12
|
const UPDATE_COMMAND_MAX_BUFFER = 10 * 1024 * 1024;
|
|
13
|
-
const OPENCLAW_UPDATE_TARGET_VERSION = '2026.5.6';
|
|
14
13
|
const CHANNEL_FALLBACK_VERSION = '1.5.9';
|
|
15
14
|
|
|
16
15
|
export const ping: MethodHandler = async (): Promise<JsonValue> => {
|
|
@@ -81,22 +80,26 @@ export const doctorFix: MethodHandler = async (_params, context: MethodContext):
|
|
|
81
80
|
}
|
|
82
81
|
};
|
|
83
82
|
|
|
84
|
-
export const openclawUpdate: MethodHandler = async (): Promise<JsonValue> => {
|
|
85
|
-
// OpenClaw core updates are intentionally not exposed by this plugin.
|
|
86
|
-
return {
|
|
87
|
-
ok: false,
|
|
88
|
-
action: 'openclawUpdate',
|
|
89
|
-
disabled: true,
|
|
90
|
-
restartRecommended: false
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
|
|
94
83
|
export const pluginSelfUpdate: MethodHandler = async (_params, context: MethodContext): Promise<JsonValue> => {
|
|
95
84
|
const result = await runOpenClawCommand(
|
|
96
85
|
['plugins', 'update', 'rol-websocket-channel'],
|
|
97
86
|
context,
|
|
98
87
|
'pluginSelfUpdate'
|
|
99
88
|
);
|
|
89
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
90
|
+
|
|
91
|
+
if (isPathSourceUpdateSkip(output)) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
action: 'pluginSelfUpdate',
|
|
95
|
+
plugin: 'rol-websocket-channel',
|
|
96
|
+
skipped: true,
|
|
97
|
+
reason: 'path-source-plugin',
|
|
98
|
+
message: 'rol-websocket-channel is installed as a local path/global plugin, so OpenClaw plugins update skipped it.',
|
|
99
|
+
restartRecommended: false,
|
|
100
|
+
...result
|
|
101
|
+
};
|
|
102
|
+
}
|
|
100
103
|
|
|
101
104
|
return {
|
|
102
105
|
ok: true,
|
|
@@ -108,17 +111,14 @@ export const pluginSelfUpdate: MethodHandler = async (_params, context: MethodCo
|
|
|
108
111
|
};
|
|
109
112
|
|
|
110
113
|
export const currentVersion: MethodHandler = async (_params, context: MethodContext): Promise<JsonValue> => {
|
|
111
|
-
const [
|
|
112
|
-
runOpenClawCommand(['--version'], context, 'currentVersion.openclaw'),
|
|
114
|
+
const [channelPackage, registryInfo] = await Promise.all([
|
|
113
115
|
readJsonFile<{ name?: string; version?: string }>(path.join(context.projectRoot, 'package.json')),
|
|
114
116
|
queryNpmRegistry('rol-websocket-channel', context)
|
|
115
117
|
]);
|
|
116
118
|
|
|
117
|
-
const openclawCurrentVersion = parseOpenClawVersion(openclawCurrentResult.stdout);
|
|
118
119
|
const channelCurrentVersion = normalizeVersion(channelPackage.version);
|
|
119
120
|
const channelLatestVersion = registryInfo.version;
|
|
120
121
|
const targets = [
|
|
121
|
-
buildExternalVersionTarget('openclaw', 'openclaw', openclawCurrentVersion),
|
|
122
122
|
buildCurrentVersionTarget(
|
|
123
123
|
'channel',
|
|
124
124
|
normalizePackageName(channelPackage.name) || 'rol-websocket-channel',
|
|
@@ -127,7 +127,7 @@ export const currentVersion: MethodHandler = async (_params, context: MethodCont
|
|
|
127
127
|
)
|
|
128
128
|
];
|
|
129
129
|
|
|
130
|
-
const upgradeTargets = targets.filter((t) => t.
|
|
130
|
+
const upgradeTargets = targets.filter((t) => t.status === 'outdated');
|
|
131
131
|
|
|
132
132
|
return {
|
|
133
133
|
ok: true,
|
|
@@ -324,59 +324,6 @@ async function runOpenClawCommand(
|
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
async function runSystemCommand(
|
|
328
|
-
command: string,
|
|
329
|
-
args: string[],
|
|
330
|
-
cwd: string,
|
|
331
|
-
context: MethodContext,
|
|
332
|
-
action: string
|
|
333
|
-
): Promise<{ command: string; args: string[]; cwd: string; stdout: string; stderr: string }> {
|
|
334
|
-
const options = buildExecOptions(cwd, context.openclawRoot);
|
|
335
|
-
|
|
336
|
-
console.log(
|
|
337
|
-
`[system] exec start: action=${action}, command=${command}, args=${JSON.stringify(args)}, cwd=${options.cwd}`
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
try {
|
|
341
|
-
const { stdout, stderr } = await execFileAsync(command, args, {
|
|
342
|
-
...options,
|
|
343
|
-
timeout: UPDATE_COMMAND_TIMEOUT_MS,
|
|
344
|
-
maxBuffer: UPDATE_COMMAND_MAX_BUFFER
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
console.log(
|
|
348
|
-
`[system] exec success: action=${action}, command=${command}, args=${JSON.stringify(args)}, stdoutLength=${stdout.length}, stderrLength=${stderr.length}`
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
command,
|
|
353
|
-
args,
|
|
354
|
-
cwd: options.cwd,
|
|
355
|
-
stdout,
|
|
356
|
-
stderr
|
|
357
|
-
};
|
|
358
|
-
} catch (err: any) {
|
|
359
|
-
const stdout = typeof err?.stdout === 'string' ? err.stdout : '';
|
|
360
|
-
const stderr = typeof err?.stderr === 'string' ? err.stderr : '';
|
|
361
|
-
console.error(
|
|
362
|
-
`[system] exec failed: action=${action}, command=${command}, args=${JSON.stringify(args)}, cwd=${options.cwd}, stdout=${JSON.stringify(stdout)}, stderr=${JSON.stringify(stderr)}`
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
throw new JsonRpcException(
|
|
366
|
-
JSON_RPC_ERRORS.internalError,
|
|
367
|
-
`OpenClaw system command failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
368
|
-
{
|
|
369
|
-
action,
|
|
370
|
-
command,
|
|
371
|
-
args,
|
|
372
|
-
cwd: options.cwd,
|
|
373
|
-
stdout,
|
|
374
|
-
stderr
|
|
375
|
-
}
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
327
|
function buildExecOptions(
|
|
381
328
|
cwd: string,
|
|
382
329
|
openclawRoot?: string
|
|
@@ -421,7 +368,6 @@ function resolveOpenClawHomeForCli(openclawRoot?: string): string | undefined {
|
|
|
421
368
|
|
|
422
369
|
interface NpmRegistryInfo {
|
|
423
370
|
version: string;
|
|
424
|
-
requiredOpenclawVersion: string | null;
|
|
425
371
|
}
|
|
426
372
|
|
|
427
373
|
async function queryNpmRegistry(
|
|
@@ -439,23 +385,18 @@ async function queryNpmRegistry(
|
|
|
439
385
|
|
|
440
386
|
const data = JSON.parse(stdout);
|
|
441
387
|
const version = typeof data.version === 'string' ? data.version : null;
|
|
442
|
-
const requiredOpenclawVersion =
|
|
443
|
-
data?.openclaw?.build?.openclawVersion ??
|
|
444
|
-
data?.openclaw?.compat?.pluginApi ??
|
|
445
|
-
null;
|
|
446
388
|
|
|
447
389
|
if (!version) {
|
|
448
390
|
console.error(`[system] queryNpmRegistry: no version field in npm view output`);
|
|
449
|
-
return { version: CHANNEL_FALLBACK_VERSION
|
|
391
|
+
return { version: CHANNEL_FALLBACK_VERSION };
|
|
450
392
|
}
|
|
451
393
|
|
|
452
|
-
return { version
|
|
394
|
+
return { version };
|
|
453
395
|
} catch (err) {
|
|
454
396
|
console.error(
|
|
455
397
|
`[system] queryNpmRegistry failed: ${err instanceof Error ? err.message : String(err)}`
|
|
456
398
|
);
|
|
457
|
-
|
|
458
|
-
return { version: CHANNEL_FALLBACK_VERSION, requiredOpenclawVersion: OPENCLAW_UPDATE_TARGET_VERSION };
|
|
399
|
+
return { version: CHANNEL_FALLBACK_VERSION };
|
|
459
400
|
}
|
|
460
401
|
}
|
|
461
402
|
|
|
@@ -475,45 +416,6 @@ function buildCurrentVersionTarget(
|
|
|
475
416
|
};
|
|
476
417
|
}
|
|
477
418
|
|
|
478
|
-
function buildExternalVersionTarget(
|
|
479
|
-
name: string,
|
|
480
|
-
packageName: string,
|
|
481
|
-
currentVersion: string
|
|
482
|
-
): Record<string, JsonValue> {
|
|
483
|
-
return {
|
|
484
|
-
name,
|
|
485
|
-
packageName,
|
|
486
|
-
currentVersion,
|
|
487
|
-
latestVersion: 'managed-externally',
|
|
488
|
-
status: 'external'
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function buildCommandStep(
|
|
493
|
-
name: string,
|
|
494
|
-
result: { command: string; args: string[]; cwd: string; stdout: string; stderr: string }
|
|
495
|
-
): Record<string, JsonValue> {
|
|
496
|
-
return {
|
|
497
|
-
name,
|
|
498
|
-
command: result.command,
|
|
499
|
-
args: result.args,
|
|
500
|
-
cwd: result.cwd,
|
|
501
|
-
stdout: result.stdout,
|
|
502
|
-
stderr: result.stderr
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function parseOpenClawVersion(stdout: string): string {
|
|
507
|
-
const match = stdout.match(/OpenClaw\s+([^\s(]+)/i);
|
|
508
|
-
if (!match) {
|
|
509
|
-
throw new JsonRpcException(JSON_RPC_ERRORS.internalError, 'Unable to parse OpenClaw version', {
|
|
510
|
-
stdout
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return normalizeVersion(match[1]);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
419
|
function normalizePackageName(value: string | undefined): string | null {
|
|
518
420
|
if (typeof value !== 'string' || !value.trim()) {
|
|
519
421
|
return null;
|
|
@@ -529,3 +431,7 @@ function normalizeVersion(value: string | undefined): string {
|
|
|
529
431
|
|
|
530
432
|
return value.trim().replace(/^v/i, '');
|
|
531
433
|
}
|
|
434
|
+
|
|
435
|
+
function isPathSourceUpdateSkip(output: string): boolean {
|
|
436
|
+
return /Skipping\s+"?rol-websocket-channel"?\s+\(source:\s*path\)/i.test(output);
|
|
437
|
+
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { afterEach, describe, test } from 'node:test';
|
|
6
|
-
import { currentVersion, openclawUpdate } from './system.js';
|
|
7
|
-
const tempDirs = [];
|
|
8
|
-
const ORIGINAL_OPENCLAW_BIN = process.env.OPENCLAW_BIN;
|
|
9
|
-
const ORIGINAL_NPM_BIN = process.env.NPM_BIN;
|
|
10
|
-
afterEach(async () => {
|
|
11
|
-
if (ORIGINAL_OPENCLAW_BIN === undefined) {
|
|
12
|
-
delete process.env.OPENCLAW_BIN;
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
process.env.OPENCLAW_BIN = ORIGINAL_OPENCLAW_BIN;
|
|
16
|
-
}
|
|
17
|
-
if (ORIGINAL_NPM_BIN === undefined) {
|
|
18
|
-
delete process.env.NPM_BIN;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
process.env.NPM_BIN = ORIGINAL_NPM_BIN;
|
|
22
|
-
}
|
|
23
|
-
while (tempDirs.length > 0) {
|
|
24
|
-
const dir = tempDirs.pop();
|
|
25
|
-
if (dir) {
|
|
26
|
-
await fs.rm(dir, { recursive: true, force: true });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
describe('currentVersion', () => {
|
|
31
|
-
test('returns current OpenClaw and channel versions without checking remote latest versions', async () => {
|
|
32
|
-
const context = await createMethodContext();
|
|
33
|
-
process.env.OPENCLAW_BIN = await createFakeOpenClawBin(context.projectRoot);
|
|
34
|
-
const result = await currentVersion({}, context);
|
|
35
|
-
const calls = await readCalls(context.projectRoot);
|
|
36
|
-
assert.equal(result.ok, true);
|
|
37
|
-
assert.equal(result.action, 'currentVersion');
|
|
38
|
-
assert.equal(result.checkOnly, true);
|
|
39
|
-
assert.equal('guaranteeLatest' in result, false);
|
|
40
|
-
assert.equal(result.restartRecommended, false);
|
|
41
|
-
assert.deepEqual(result.targets, [
|
|
42
|
-
{
|
|
43
|
-
name: 'openclaw',
|
|
44
|
-
packageName: 'openclaw',
|
|
45
|
-
currentVersion: '2026.5.7',
|
|
46
|
-
status: 'current'
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: 'channel',
|
|
50
|
-
packageName: 'rol-websocket-channel',
|
|
51
|
-
currentVersion: '1.4.8',
|
|
52
|
-
status: 'current'
|
|
53
|
-
}
|
|
54
|
-
]);
|
|
55
|
-
assert.equal(calls.some((call) => call.includes('update')), false);
|
|
56
|
-
assert.deepEqual(calls, [
|
|
57
|
-
['openclaw', '--version']
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
describe('openclawUpdate', () => {
|
|
62
|
-
test('installs the fixed OpenClaw core version and verifies it', async () => {
|
|
63
|
-
const context = await createMethodContext();
|
|
64
|
-
process.env.OPENCLAW_BIN = await createFakeOpenClawBin(context.projectRoot);
|
|
65
|
-
process.env.NPM_BIN = await createFakeNpmBin(context.projectRoot);
|
|
66
|
-
const result = await openclawUpdate({}, context);
|
|
67
|
-
const calls = await readCalls(context.projectRoot);
|
|
68
|
-
assert.equal(result.ok, true);
|
|
69
|
-
assert.equal(result.action, 'openclawUpdate');
|
|
70
|
-
assert.equal(result.targetPackage, '@openclaw/core');
|
|
71
|
-
assert.equal(result.targetVersion, '2026.5.6');
|
|
72
|
-
assert.equal(result.restartRecommended, true);
|
|
73
|
-
assert.deepEqual(result.steps.map((step) => [step.name, step.args]), [
|
|
74
|
-
['installCore', ['install', '-g', '@openclaw/core@2026.5.6']],
|
|
75
|
-
['version', ['--version']],
|
|
76
|
-
['doctorDeep', ['doctor', '--deep']]
|
|
77
|
-
]);
|
|
78
|
-
assert.deepEqual(calls, [
|
|
79
|
-
['npm', 'install', '-g', '@openclaw/core@2026.5.6'],
|
|
80
|
-
['openclaw', '--version'],
|
|
81
|
-
['openclaw', 'doctor', '--deep']
|
|
82
|
-
]);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
async function createMethodContext() {
|
|
86
|
-
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rol-system-'));
|
|
87
|
-
tempDirs.push(dir);
|
|
88
|
-
await fs.writeFile(path.join(dir, 'package.json'), `${JSON.stringify({
|
|
89
|
-
name: 'rol-websocket-channel',
|
|
90
|
-
version: '1.4.8'
|
|
91
|
-
}, null, 2)}\n`, 'utf8');
|
|
92
|
-
return {
|
|
93
|
-
projectRoot: dir,
|
|
94
|
-
openclawRoot: dir
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
async function createFakeOpenClawBin(root) {
|
|
98
|
-
if (process.platform === 'win32') {
|
|
99
|
-
const scriptPath = path.join(root, 'fake-openclaw.cmd');
|
|
100
|
-
await fs.writeFile(scriptPath, [
|
|
101
|
-
'@echo off',
|
|
102
|
-
`node "%~dp0fake-openclaw.js" %*`
|
|
103
|
-
].join('\r\n'), 'utf8');
|
|
104
|
-
await createFakeOpenClawJs(root);
|
|
105
|
-
return scriptPath;
|
|
106
|
-
}
|
|
107
|
-
const scriptPath = path.join(root, 'fake-openclaw');
|
|
108
|
-
await fs.writeFile(scriptPath, [
|
|
109
|
-
'#!/bin/sh',
|
|
110
|
-
'node "$(dirname "$0")/fake-openclaw.js" "$@"'
|
|
111
|
-
].join('\n'), 'utf8');
|
|
112
|
-
await fs.chmod(scriptPath, 0o755);
|
|
113
|
-
await createFakeOpenClawJs(root);
|
|
114
|
-
return scriptPath;
|
|
115
|
-
}
|
|
116
|
-
async function createFakeOpenClawJs(root) {
|
|
117
|
-
await fs.writeFile(path.join(root, 'fake-openclaw.js'), [
|
|
118
|
-
"import fs from 'node:fs';",
|
|
119
|
-
"import path from 'node:path';",
|
|
120
|
-
'const file = path.join(process.cwd(), "calls.ndjson");',
|
|
121
|
-
'fs.appendFileSync(file, `${JSON.stringify(["openclaw", ...process.argv.slice(2)])}\\n`);',
|
|
122
|
-
'console.log("OpenClaw 2026.5.7 (eeef486)");'
|
|
123
|
-
].join('\n'), 'utf8');
|
|
124
|
-
}
|
|
125
|
-
async function createFakeNpmBin(root) {
|
|
126
|
-
if (process.platform === 'win32') {
|
|
127
|
-
const scriptPath = path.join(root, 'fake-npm.cmd');
|
|
128
|
-
await fs.writeFile(scriptPath, [
|
|
129
|
-
'@echo off',
|
|
130
|
-
`node "%~dp0fake-npm.js" %*`
|
|
131
|
-
].join('\r\n'), 'utf8');
|
|
132
|
-
await createFakeNpmJs(root);
|
|
133
|
-
return scriptPath;
|
|
134
|
-
}
|
|
135
|
-
const scriptPath = path.join(root, 'fake-npm');
|
|
136
|
-
await fs.writeFile(scriptPath, [
|
|
137
|
-
'#!/bin/sh',
|
|
138
|
-
'node "$(dirname "$0")/fake-npm.js" "$@"'
|
|
139
|
-
].join('\n'), 'utf8');
|
|
140
|
-
await fs.chmod(scriptPath, 0o755);
|
|
141
|
-
await createFakeNpmJs(root);
|
|
142
|
-
return scriptPath;
|
|
143
|
-
}
|
|
144
|
-
async function createFakeNpmJs(root) {
|
|
145
|
-
await fs.writeFile(path.join(root, 'fake-npm.js'), [
|
|
146
|
-
"import fs from 'node:fs';",
|
|
147
|
-
"import path from 'node:path';",
|
|
148
|
-
'const args = process.argv.slice(2);',
|
|
149
|
-
'const file = path.join(process.cwd(), "calls.ndjson");',
|
|
150
|
-
'fs.appendFileSync(file, `${JSON.stringify(["npm", ...args])}\\n`);',
|
|
151
|
-
'if (args.join(" ") === "view openclaw version --json") {',
|
|
152
|
-
' console.log(JSON.stringify("2026.5.7"));',
|
|
153
|
-
'} else if (args.join(" ") === "view rol-websocket-channel version --json") {',
|
|
154
|
-
' console.log(JSON.stringify("1.4.8"));',
|
|
155
|
-
'} else if (args.join(" ") === "install -g @openclaw/core@2026.5.6") {',
|
|
156
|
-
' console.log("installed @openclaw/core@2026.5.6");',
|
|
157
|
-
'} else {',
|
|
158
|
-
' console.error(`Unexpected npm args: ${args.join(" ")}`);',
|
|
159
|
-
' process.exit(1);',
|
|
160
|
-
'}'
|
|
161
|
-
].join('\n'), 'utf8');
|
|
162
|
-
}
|
|
163
|
-
async function readCalls(root) {
|
|
164
|
-
const content = await fs.readFile(path.join(root, 'calls.ndjson'), 'utf8');
|
|
165
|
-
return content.trim().split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
|
|
166
|
-
}
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { afterEach, describe, test } from 'node:test';
|
|
6
|
-
|
|
7
|
-
import { currentVersion, openclawUpdate } from './system.js';
|
|
8
|
-
import type { MethodContext } from '../types.js';
|
|
9
|
-
|
|
10
|
-
const tempDirs: string[] = [];
|
|
11
|
-
const ORIGINAL_OPENCLAW_BIN = process.env.OPENCLAW_BIN;
|
|
12
|
-
const ORIGINAL_NPM_BIN = process.env.NPM_BIN;
|
|
13
|
-
|
|
14
|
-
afterEach(async () => {
|
|
15
|
-
if (ORIGINAL_OPENCLAW_BIN === undefined) {
|
|
16
|
-
delete process.env.OPENCLAW_BIN;
|
|
17
|
-
} else {
|
|
18
|
-
process.env.OPENCLAW_BIN = ORIGINAL_OPENCLAW_BIN;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (ORIGINAL_NPM_BIN === undefined) {
|
|
22
|
-
delete process.env.NPM_BIN;
|
|
23
|
-
} else {
|
|
24
|
-
process.env.NPM_BIN = ORIGINAL_NPM_BIN;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
while (tempDirs.length > 0) {
|
|
28
|
-
const dir = tempDirs.pop();
|
|
29
|
-
if (dir) {
|
|
30
|
-
await fs.rm(dir, { recursive: true, force: true });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('currentVersion', () => {
|
|
36
|
-
test('returns current OpenClaw and channel versions without checking remote latest versions', async () => {
|
|
37
|
-
const context = await createMethodContext();
|
|
38
|
-
process.env.OPENCLAW_BIN = await createFakeOpenClawBin(context.projectRoot);
|
|
39
|
-
|
|
40
|
-
const result = await currentVersion({}, context) as {
|
|
41
|
-
ok: boolean;
|
|
42
|
-
action: string;
|
|
43
|
-
checkOnly: boolean;
|
|
44
|
-
guaranteeLatest?: boolean;
|
|
45
|
-
targets: Array<{
|
|
46
|
-
name: string;
|
|
47
|
-
packageName: string;
|
|
48
|
-
currentVersion: string;
|
|
49
|
-
status: string;
|
|
50
|
-
}>;
|
|
51
|
-
restartRecommended: boolean;
|
|
52
|
-
};
|
|
53
|
-
const calls = await readCalls(context.projectRoot);
|
|
54
|
-
|
|
55
|
-
assert.equal(result.ok, true);
|
|
56
|
-
assert.equal(result.action, 'currentVersion');
|
|
57
|
-
assert.equal(result.checkOnly, true);
|
|
58
|
-
assert.equal('guaranteeLatest' in result, false);
|
|
59
|
-
assert.equal(result.restartRecommended, false);
|
|
60
|
-
assert.deepEqual(result.targets, [
|
|
61
|
-
{
|
|
62
|
-
name: 'openclaw',
|
|
63
|
-
packageName: 'openclaw',
|
|
64
|
-
currentVersion: '2026.5.7',
|
|
65
|
-
status: 'current'
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: 'channel',
|
|
69
|
-
packageName: 'rol-websocket-channel',
|
|
70
|
-
currentVersion: '1.4.8',
|
|
71
|
-
status: 'current'
|
|
72
|
-
}
|
|
73
|
-
]);
|
|
74
|
-
assert.equal(
|
|
75
|
-
calls.some((call: string[]) => call.includes('update')),
|
|
76
|
-
false
|
|
77
|
-
);
|
|
78
|
-
assert.deepEqual(calls, [
|
|
79
|
-
['openclaw', '--version']
|
|
80
|
-
]);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('openclawUpdate', () => {
|
|
85
|
-
test('installs the fixed OpenClaw core version and verifies it', async () => {
|
|
86
|
-
const context = await createMethodContext();
|
|
87
|
-
process.env.OPENCLAW_BIN = await createFakeOpenClawBin(context.projectRoot);
|
|
88
|
-
process.env.NPM_BIN = await createFakeNpmBin(context.projectRoot);
|
|
89
|
-
|
|
90
|
-
const result = await openclawUpdate({}, context) as {
|
|
91
|
-
ok: boolean;
|
|
92
|
-
action: string;
|
|
93
|
-
targetPackage: string;
|
|
94
|
-
targetVersion: string;
|
|
95
|
-
restartRecommended: boolean;
|
|
96
|
-
steps: Array<{
|
|
97
|
-
name: string;
|
|
98
|
-
args: string[];
|
|
99
|
-
}>;
|
|
100
|
-
};
|
|
101
|
-
const calls = await readCalls(context.projectRoot);
|
|
102
|
-
|
|
103
|
-
assert.equal(result.ok, true);
|
|
104
|
-
assert.equal(result.action, 'openclawUpdate');
|
|
105
|
-
assert.equal(result.targetPackage, '@openclaw/core');
|
|
106
|
-
assert.equal(result.targetVersion, '2026.5.6');
|
|
107
|
-
assert.equal(result.restartRecommended, true);
|
|
108
|
-
assert.deepEqual(result.steps.map((step) => [step.name, step.args]), [
|
|
109
|
-
['installCore', ['install', '-g', '@openclaw/core@2026.5.6']],
|
|
110
|
-
['version', ['--version']],
|
|
111
|
-
['doctorDeep', ['doctor', '--deep']]
|
|
112
|
-
]);
|
|
113
|
-
assert.deepEqual(calls, [
|
|
114
|
-
['npm', 'install', '-g', '@openclaw/core@2026.5.6'],
|
|
115
|
-
['openclaw', '--version'],
|
|
116
|
-
['openclaw', 'doctor', '--deep']
|
|
117
|
-
]);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
async function createMethodContext(): Promise<MethodContext> {
|
|
122
|
-
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rol-system-'));
|
|
123
|
-
tempDirs.push(dir);
|
|
124
|
-
await fs.writeFile(path.join(dir, 'package.json'), `${JSON.stringify({
|
|
125
|
-
name: 'rol-websocket-channel',
|
|
126
|
-
version: '1.4.8'
|
|
127
|
-
}, null, 2)}\n`, 'utf8');
|
|
128
|
-
return {
|
|
129
|
-
projectRoot: dir,
|
|
130
|
-
openclawRoot: dir
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function createFakeOpenClawBin(root: string): Promise<string> {
|
|
135
|
-
if (process.platform === 'win32') {
|
|
136
|
-
const scriptPath = path.join(root, 'fake-openclaw.cmd');
|
|
137
|
-
await fs.writeFile(
|
|
138
|
-
scriptPath,
|
|
139
|
-
[
|
|
140
|
-
'@echo off',
|
|
141
|
-
`node "%~dp0fake-openclaw.js" %*`
|
|
142
|
-
].join('\r\n'),
|
|
143
|
-
'utf8'
|
|
144
|
-
);
|
|
145
|
-
await createFakeOpenClawJs(root);
|
|
146
|
-
return scriptPath;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const scriptPath = path.join(root, 'fake-openclaw');
|
|
150
|
-
await fs.writeFile(
|
|
151
|
-
scriptPath,
|
|
152
|
-
[
|
|
153
|
-
'#!/bin/sh',
|
|
154
|
-
'node "$(dirname "$0")/fake-openclaw.js" "$@"'
|
|
155
|
-
].join('\n'),
|
|
156
|
-
'utf8'
|
|
157
|
-
);
|
|
158
|
-
await fs.chmod(scriptPath, 0o755);
|
|
159
|
-
await createFakeOpenClawJs(root);
|
|
160
|
-
return scriptPath;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async function createFakeOpenClawJs(root: string): Promise<void> {
|
|
164
|
-
await fs.writeFile(
|
|
165
|
-
path.join(root, 'fake-openclaw.js'),
|
|
166
|
-
[
|
|
167
|
-
"import fs from 'node:fs';",
|
|
168
|
-
"import path from 'node:path';",
|
|
169
|
-
'const file = path.join(process.cwd(), "calls.ndjson");',
|
|
170
|
-
'fs.appendFileSync(file, `${JSON.stringify(["openclaw", ...process.argv.slice(2)])}\\n`);',
|
|
171
|
-
'console.log("OpenClaw 2026.5.7 (eeef486)");'
|
|
172
|
-
].join('\n'),
|
|
173
|
-
'utf8'
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async function createFakeNpmBin(root: string): Promise<string> {
|
|
178
|
-
if (process.platform === 'win32') {
|
|
179
|
-
const scriptPath = path.join(root, 'fake-npm.cmd');
|
|
180
|
-
await fs.writeFile(
|
|
181
|
-
scriptPath,
|
|
182
|
-
[
|
|
183
|
-
'@echo off',
|
|
184
|
-
`node "%~dp0fake-npm.js" %*`
|
|
185
|
-
].join('\r\n'),
|
|
186
|
-
'utf8'
|
|
187
|
-
);
|
|
188
|
-
await createFakeNpmJs(root);
|
|
189
|
-
return scriptPath;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const scriptPath = path.join(root, 'fake-npm');
|
|
193
|
-
await fs.writeFile(
|
|
194
|
-
scriptPath,
|
|
195
|
-
[
|
|
196
|
-
'#!/bin/sh',
|
|
197
|
-
'node "$(dirname "$0")/fake-npm.js" "$@"'
|
|
198
|
-
].join('\n'),
|
|
199
|
-
'utf8'
|
|
200
|
-
);
|
|
201
|
-
await fs.chmod(scriptPath, 0o755);
|
|
202
|
-
await createFakeNpmJs(root);
|
|
203
|
-
return scriptPath;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function createFakeNpmJs(root: string): Promise<void> {
|
|
207
|
-
await fs.writeFile(
|
|
208
|
-
path.join(root, 'fake-npm.js'),
|
|
209
|
-
[
|
|
210
|
-
"import fs from 'node:fs';",
|
|
211
|
-
"import path from 'node:path';",
|
|
212
|
-
'const args = process.argv.slice(2);',
|
|
213
|
-
'const file = path.join(process.cwd(), "calls.ndjson");',
|
|
214
|
-
'fs.appendFileSync(file, `${JSON.stringify(["npm", ...args])}\\n`);',
|
|
215
|
-
'if (args.join(" ") === "view openclaw version --json") {',
|
|
216
|
-
' console.log(JSON.stringify("2026.5.7"));',
|
|
217
|
-
'} else if (args.join(" ") === "view rol-websocket-channel version --json") {',
|
|
218
|
-
' console.log(JSON.stringify("1.4.8"));',
|
|
219
|
-
'} else if (args.join(" ") === "install -g @openclaw/core@2026.5.6") {',
|
|
220
|
-
' console.log("installed @openclaw/core@2026.5.6");',
|
|
221
|
-
'} else {',
|
|
222
|
-
' console.error(`Unexpected npm args: ${args.join(" ")}`);',
|
|
223
|
-
' process.exit(1);',
|
|
224
|
-
'}'
|
|
225
|
-
].join('\n'),
|
|
226
|
-
'utf8'
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
async function readCalls(root: string): Promise<string[][]> {
|
|
231
|
-
const content = await fs.readFile(path.join(root, 'calls.ndjson'), 'utf8');
|
|
232
|
-
return content.trim().split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line) as string[]);
|
|
233
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import test from 'node:test';
|
|
3
|
-
|
|
4
|
-
import { handleCustomMessageType } from '../index.js';
|
|
5
|
-
import { messageHandler } from '../message-handler.js';
|
|
6
|
-
import {
|
|
7
|
-
_setMqttConnectFn,
|
|
8
|
-
closeGlobalConnection,
|
|
9
|
-
createGlobalMqttConnection
|
|
10
|
-
} from '../src/mqtt/connection-manager.js';
|
|
11
|
-
|
|
12
|
-
test('openclawUpdate publishes an immediate running response before the command finishes', async () => {
|
|
13
|
-
const published: Array<{ topic: string; message: string }> = [];
|
|
14
|
-
const originalOpenclawUpdate = (messageHandler as any).openclawUpdate;
|
|
15
|
-
let finish!: () => void;
|
|
16
|
-
const commandFinished = new Promise<void>((resolve) => {
|
|
17
|
-
finish = resolve;
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const fakeClient = {
|
|
21
|
-
connected: true,
|
|
22
|
-
on() {},
|
|
23
|
-
subscribe() {},
|
|
24
|
-
end() {},
|
|
25
|
-
publish(topic: string, message: string) {
|
|
26
|
-
published.push({ topic, message });
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
_setMqttConnectFn(() => fakeClient as any);
|
|
31
|
-
await createGlobalMqttConnection('mqtt://test', 'announcement/user/agent/#', {});
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
(messageHandler as any).openclawUpdate = async () => {
|
|
35
|
-
await commandFinished;
|
|
36
|
-
return { ok: true, result: { ok: true, action: 'openclawUpdate' } };
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const task = handleCustomMessageType(
|
|
40
|
-
'openclawUpdate',
|
|
41
|
-
{ source_type: 'device' },
|
|
42
|
-
'trace-update-001',
|
|
43
|
-
'default',
|
|
44
|
-
'announcement/user/agent/#'
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
48
|
-
|
|
49
|
-
assert.equal(published.length, 1);
|
|
50
|
-
const first = JSON.parse(published[0]!.message);
|
|
51
|
-
assert.equal(published[0]!.topic, 'announcement/user/agent/device');
|
|
52
|
-
assert.equal(first.trace_id, 'trace-update-001');
|
|
53
|
-
assert.equal(first.success, true);
|
|
54
|
-
assert.equal(first.data.status, 'running');
|
|
55
|
-
|
|
56
|
-
await task;
|
|
57
|
-
finish();
|
|
58
|
-
await waitFor(() => published.length === 2);
|
|
59
|
-
|
|
60
|
-
assert.equal(published.length, 2);
|
|
61
|
-
const final = JSON.parse(published[1]!.message);
|
|
62
|
-
assert.equal(final.trace_id, 'trace-update-001');
|
|
63
|
-
assert.equal(final.success, true);
|
|
64
|
-
assert.deepEqual(final.data, { ok: true, action: 'openclawUpdate' });
|
|
65
|
-
} finally {
|
|
66
|
-
(messageHandler as any).openclawUpdate = originalOpenclawUpdate;
|
|
67
|
-
closeGlobalConnection();
|
|
68
|
-
_setMqttConnectFn(null);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
async function waitFor(predicate: () => boolean): Promise<void> {
|
|
73
|
-
for (let i = 0; i < 20; i += 1) {
|
|
74
|
-
if (predicate()) return;
|
|
75
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
76
|
-
}
|
|
77
|
-
}
|