rol-websocket-channel 1.5.1 → 1.5.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/{MQTT-API 5-6.md → MQTT-API System.md } +112 -1
- package/dist/index.js +16 -7
- package/dist/src/admin/cli-manifest.test.js +24 -1
- package/dist/src/admin/methods/mem9.js +10 -0
- package/dist/src/admin/methods/system.js +2 -2
- package/index.ts +23 -12
- package/package.json +1 -1
- package/src/admin/cli-manifest.test.ts +28 -1
- package/src/admin/methods/mem9.ts +10 -0
- package/src/admin/methods/system.ts +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# OpenClaw MQTT API 文档
|
|
1
|
+
# OpenClaw MQTT API 文档 (System 更新接口)
|
|
2
2
|
|
|
3
3
|
## 消息格式
|
|
4
4
|
|
|
@@ -1797,3 +1797,114 @@ MQTT 请求示例:
|
|
|
1797
1797
|
- `category === "document"` 时,可以作为文档展示或下载。
|
|
1798
1798
|
- `storageStatus === "local_only"` 时,先调 `artifactsEnsureUploaded` 获取可访问的 `downloadUrl`。
|
|
1799
1799
|
- `storageStatus === "uploaded"` 且 `fileUrl` 存在时,可以直接使用 `fileUrl` 展示或下载。
|
|
1800
|
+
|
|
1801
|
+
---
|
|
1802
|
+
|
|
1803
|
+
## System 更新接口
|
|
1804
|
+
|
|
1805
|
+
这两个接口用于 App 按钮触发本机 OpenClaw 或当前插件更新。请求通过 MQTT 发送到当前已配对设备,插件会在本机执行对应的 `openclaw` 命令。
|
|
1806
|
+
|
|
1807
|
+
### openclawUpdate
|
|
1808
|
+
|
|
1809
|
+
作用:升级本机 OpenClaw。
|
|
1810
|
+
|
|
1811
|
+
实际执行命令:
|
|
1812
|
+
|
|
1813
|
+
```bash
|
|
1814
|
+
openclaw update
|
|
1815
|
+
```
|
|
1816
|
+
|
|
1817
|
+
MQTT 请求示例:
|
|
1818
|
+
|
|
1819
|
+
```json
|
|
1820
|
+
{
|
|
1821
|
+
"type": "openclawUpdate",
|
|
1822
|
+
"trace_id": "openclaw-update-001",
|
|
1823
|
+
"data": {}
|
|
1824
|
+
}
|
|
1825
|
+
```
|
|
1826
|
+
|
|
1827
|
+
成功返回示例:
|
|
1828
|
+
|
|
1829
|
+
```json
|
|
1830
|
+
{
|
|
1831
|
+
"type": "receiver",
|
|
1832
|
+
"trace_id": "openclaw-update-001",
|
|
1833
|
+
"source": "system",
|
|
1834
|
+
"success": true,
|
|
1835
|
+
"data": {
|
|
1836
|
+
"ok": true,
|
|
1837
|
+
"action": "openclawUpdate",
|
|
1838
|
+
"restartRecommended": true,
|
|
1839
|
+
"command": "openclaw",
|
|
1840
|
+
"args": ["update"],
|
|
1841
|
+
"cwd": "/home/woowonjae/.openclaw",
|
|
1842
|
+
"stdout": "...",
|
|
1843
|
+
"stderr": ""
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
```
|
|
1847
|
+
|
|
1848
|
+
### pluginSelfUpdate
|
|
1849
|
+
|
|
1850
|
+
作用:升级当前 `rol-websocket-channel` 插件。
|
|
1851
|
+
|
|
1852
|
+
实际执行命令:
|
|
1853
|
+
|
|
1854
|
+
```bash
|
|
1855
|
+
openclaw plugins update rol-websocket-channel
|
|
1856
|
+
```
|
|
1857
|
+
|
|
1858
|
+
MQTT 请求示例:
|
|
1859
|
+
|
|
1860
|
+
```json
|
|
1861
|
+
{
|
|
1862
|
+
"type": "pluginSelfUpdate",
|
|
1863
|
+
"trace_id": "plugin-self-update-001",
|
|
1864
|
+
"data": {}
|
|
1865
|
+
}
|
|
1866
|
+
```
|
|
1867
|
+
|
|
1868
|
+
成功返回示例:
|
|
1869
|
+
|
|
1870
|
+
```json
|
|
1871
|
+
{
|
|
1872
|
+
"type": "receiver",
|
|
1873
|
+
"trace_id": "plugin-self-update-001",
|
|
1874
|
+
"source": "system",
|
|
1875
|
+
"success": true,
|
|
1876
|
+
"data": {
|
|
1877
|
+
"ok": true,
|
|
1878
|
+
"action": "pluginSelfUpdate",
|
|
1879
|
+
"plugin": "rol-websocket-channel",
|
|
1880
|
+
"restartRecommended": true,
|
|
1881
|
+
"command": "openclaw",
|
|
1882
|
+
"args": ["plugins", "update", "rol-websocket-channel"],
|
|
1883
|
+
"cwd": "/home/woowonjae/.openclaw",
|
|
1884
|
+
"stdout": "...",
|
|
1885
|
+
"stderr": ""
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
```
|
|
1889
|
+
|
|
1890
|
+
### 失败返回
|
|
1891
|
+
|
|
1892
|
+
如果本机命令执行失败,`success` 会是 `false`,错误信息里会带上实际执行的命令、参数、工作目录、`stdout` 和 `stderr`,前端可以直接展示 `error.message` 或 `error.stderr`。
|
|
1893
|
+
|
|
1894
|
+
```json
|
|
1895
|
+
{
|
|
1896
|
+
"type": "receiver",
|
|
1897
|
+
"trace_id": "plugin-self-update-001",
|
|
1898
|
+
"source": "system",
|
|
1899
|
+
"success": false,
|
|
1900
|
+
"error": {
|
|
1901
|
+
"message": "OpenClaw system command failed: Command failed: openclaw plugins update rol-websocket-channel",
|
|
1902
|
+
"action": "pluginSelfUpdate",
|
|
1903
|
+
"command": "openclaw",
|
|
1904
|
+
"args": ["plugins", "update", "rol-websocket-channel"],
|
|
1905
|
+
"cwd": "/home/woowonjae/.openclaw",
|
|
1906
|
+
"stdout": "...",
|
|
1907
|
+
"stderr": "..."
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,21 @@ let pluginRuntime = null;
|
|
|
12
12
|
export function getPluginRuntime() {
|
|
13
13
|
return pluginRuntime;
|
|
14
14
|
}
|
|
15
|
+
export function formatCliErrorPayload(error) {
|
|
16
|
+
const data = error?.data;
|
|
17
|
+
const dataObject = data && typeof data === "object" && !Array.isArray(data)
|
|
18
|
+
? data
|
|
19
|
+
: {};
|
|
20
|
+
const code = dataObject.code ?? error?.code;
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
error: {
|
|
24
|
+
message: error instanceof Error ? error.message : String(error),
|
|
25
|
+
...dataObject,
|
|
26
|
+
...(code === undefined ? {} : { code }),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
15
30
|
// ============================================
|
|
16
31
|
// 4. 插件主体定义
|
|
17
32
|
// ============================================
|
|
@@ -554,13 +569,7 @@ function registerAdminBridgeCli(api) {
|
|
|
554
569
|
}
|
|
555
570
|
catch (error) {
|
|
556
571
|
process.exitCode = 1;
|
|
557
|
-
process.stderr.write(JSON.stringify(
|
|
558
|
-
ok: false,
|
|
559
|
-
error: {
|
|
560
|
-
message: error instanceof Error ? error.message : String(error),
|
|
561
|
-
code: error?.data?.code,
|
|
562
|
-
},
|
|
563
|
-
}, null, 2) + "\n");
|
|
572
|
+
process.stderr.write(JSON.stringify(formatCliErrorPayload(error), null, 2) + "\n");
|
|
564
573
|
}
|
|
565
574
|
});
|
|
566
575
|
const mem9 = root
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import test from 'node:test';
|
|
4
|
-
import register from '../../index.js';
|
|
4
|
+
import register, { formatCliErrorPayload } from '../../index.js';
|
|
5
5
|
const manifest = JSON.parse(readFileSync(new URL('../../openclaw.plugin.json', import.meta.url), 'utf8'));
|
|
6
6
|
test('manifest declares OpenClaw 2026 command ownership for admin bridge CLI', () => {
|
|
7
7
|
assert.deepEqual(manifest.activation?.onCommands, ['admin-bridge', 'rol-websocket-channel']);
|
|
@@ -60,3 +60,26 @@ test('runtime CLI registrar exposes admin-bridge and rol-websocket-channel comma
|
|
|
60
60
|
assert.equal(commands[0], 'admin-bridge');
|
|
61
61
|
assert.equal(aliases[0], 'rol-websocket-channel');
|
|
62
62
|
});
|
|
63
|
+
test('CLI error formatter preserves diagnostic data for pairing failures', () => {
|
|
64
|
+
const error = new Error('mqttUrl is missing from pairing payload');
|
|
65
|
+
error.data = {
|
|
66
|
+
code: 'PAIR_CHANNEL_CONFIG_INVALID',
|
|
67
|
+
debug: {
|
|
68
|
+
rootKeys: ['channel'],
|
|
69
|
+
channelConfigKeys: ['mqtt_topic'],
|
|
70
|
+
hasExistingMqttUrl: false
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
assert.deepEqual(formatCliErrorPayload(error), {
|
|
74
|
+
ok: false,
|
|
75
|
+
error: {
|
|
76
|
+
message: 'mqttUrl is missing from pairing payload',
|
|
77
|
+
code: 'PAIR_CHANNEL_CONFIG_INVALID',
|
|
78
|
+
debug: {
|
|
79
|
+
rootKeys: ['channel'],
|
|
80
|
+
channelConfigKeys: ['mqtt_topic'],
|
|
81
|
+
hasExistingMqttUrl: false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -211,6 +211,10 @@ async function writeMem9Config(openclawRoot, apiKey) {
|
|
|
211
211
|
config.plugins.entries[MEM9_PLUGIN_ID] = {
|
|
212
212
|
...existingEntry,
|
|
213
213
|
enabled: true,
|
|
214
|
+
hooks: {
|
|
215
|
+
...(isRecord(existingEntry.hooks) ? existingEntry.hooks : {}),
|
|
216
|
+
allowConversationAccess: true
|
|
217
|
+
},
|
|
214
218
|
config: {
|
|
215
219
|
...existingPluginConfig,
|
|
216
220
|
apiUrl: MEM9_API_URL,
|
|
@@ -221,6 +225,7 @@ async function writeMem9Config(openclawRoot, apiKey) {
|
|
|
221
225
|
await writeJsonFile(configPath, config);
|
|
222
226
|
return [
|
|
223
227
|
hadExistingKey ? 'plugins.entries.mem9.config.apiKey (replaced)' : 'plugins.entries.mem9',
|
|
228
|
+
'plugins.entries.mem9.hooks.allowConversationAccess',
|
|
224
229
|
'plugins.slots.memory'
|
|
225
230
|
];
|
|
226
231
|
}
|
|
@@ -240,6 +245,10 @@ async function ensureMem9SlotConfig(openclawRoot, apiKey) {
|
|
|
240
245
|
config.plugins.entries[MEM9_PLUGIN_ID] = {
|
|
241
246
|
...existingEntry,
|
|
242
247
|
enabled: true,
|
|
248
|
+
hooks: {
|
|
249
|
+
...(isRecord(existingEntry.hooks) ? existingEntry.hooks : {}),
|
|
250
|
+
allowConversationAccess: true
|
|
251
|
+
},
|
|
243
252
|
config: {
|
|
244
253
|
...existingPluginConfig,
|
|
245
254
|
apiUrl: MEM9_API_URL,
|
|
@@ -250,6 +259,7 @@ async function ensureMem9SlotConfig(openclawRoot, apiKey) {
|
|
|
250
259
|
await writeJsonFile(configPath, config);
|
|
251
260
|
return [
|
|
252
261
|
'plugins.entries.mem9',
|
|
262
|
+
'plugins.entries.mem9.hooks.allowConversationAccess',
|
|
253
263
|
'plugins.slots.memory'
|
|
254
264
|
];
|
|
255
265
|
}
|
|
@@ -81,11 +81,11 @@ export const openclawUpdate = async (_params, context) => {
|
|
|
81
81
|
};
|
|
82
82
|
};
|
|
83
83
|
export const pluginSelfUpdate = async (_params, context) => {
|
|
84
|
-
const result = await runOpenClawCommand(['plugins', '
|
|
84
|
+
const result = await runOpenClawCommand(['plugins', 'update', 'rol-websocket-channel'], context, 'pluginSelfUpdate');
|
|
85
85
|
return {
|
|
86
86
|
ok: true,
|
|
87
87
|
action: 'pluginSelfUpdate',
|
|
88
|
-
plugin: '
|
|
88
|
+
plugin: 'rol-websocket-channel',
|
|
89
89
|
restartRecommended: true,
|
|
90
90
|
...result
|
|
91
91
|
};
|
package/index.ts
CHANGED
|
@@ -38,6 +38,28 @@ export function getPluginRuntime(): any {
|
|
|
38
38
|
return pluginRuntime;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export function formatCliErrorPayload(error: unknown): {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: Record<string, unknown>;
|
|
44
|
+
} {
|
|
45
|
+
const data = (error as { data?: unknown } | null | undefined)?.data;
|
|
46
|
+
const dataObject =
|
|
47
|
+
data && typeof data === "object" && !Array.isArray(data)
|
|
48
|
+
? (data as Record<string, unknown>)
|
|
49
|
+
: {};
|
|
50
|
+
const code =
|
|
51
|
+
dataObject.code ?? (error as { code?: unknown } | null | undefined)?.code;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
error: {
|
|
56
|
+
message: error instanceof Error ? error.message : String(error),
|
|
57
|
+
...dataObject,
|
|
58
|
+
...(code === undefined ? {} : { code }),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
// ============================================
|
|
42
64
|
// 4. 插件主体定义
|
|
43
65
|
// ============================================
|
|
@@ -725,18 +747,7 @@ function registerAdminBridgeCli(api: any) {
|
|
|
725
747
|
} catch (error) {
|
|
726
748
|
process.exitCode = 1;
|
|
727
749
|
process.stderr.write(
|
|
728
|
-
JSON.stringify(
|
|
729
|
-
{
|
|
730
|
-
ok: false,
|
|
731
|
-
error: {
|
|
732
|
-
message:
|
|
733
|
-
error instanceof Error ? error.message : String(error),
|
|
734
|
-
code: (error as any)?.data?.code,
|
|
735
|
-
},
|
|
736
|
-
},
|
|
737
|
-
null,
|
|
738
|
-
2,
|
|
739
|
-
) + "\n",
|
|
750
|
+
JSON.stringify(formatCliErrorPayload(error), null, 2) + "\n",
|
|
740
751
|
);
|
|
741
752
|
}
|
|
742
753
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import test from 'node:test';
|
|
4
|
-
import register from '../../index.js';
|
|
4
|
+
import register, { formatCliErrorPayload } from '../../index.js';
|
|
5
5
|
|
|
6
6
|
const manifest = JSON.parse(readFileSync(new URL('../../openclaw.plugin.json', import.meta.url), 'utf8'));
|
|
7
7
|
|
|
@@ -65,3 +65,30 @@ test('runtime CLI registrar exposes admin-bridge and rol-websocket-channel comma
|
|
|
65
65
|
assert.equal(commands[0], 'admin-bridge');
|
|
66
66
|
assert.equal(aliases[0], 'rol-websocket-channel');
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
test('CLI error formatter preserves diagnostic data for pairing failures', () => {
|
|
70
|
+
const error = new Error('mqttUrl is missing from pairing payload') as Error & {
|
|
71
|
+
data: Record<string, unknown>;
|
|
72
|
+
};
|
|
73
|
+
error.data = {
|
|
74
|
+
code: 'PAIR_CHANNEL_CONFIG_INVALID',
|
|
75
|
+
debug: {
|
|
76
|
+
rootKeys: ['channel'],
|
|
77
|
+
channelConfigKeys: ['mqtt_topic'],
|
|
78
|
+
hasExistingMqttUrl: false
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
assert.deepEqual(formatCliErrorPayload(error), {
|
|
83
|
+
ok: false,
|
|
84
|
+
error: {
|
|
85
|
+
message: 'mqttUrl is missing from pairing payload',
|
|
86
|
+
code: 'PAIR_CHANNEL_CONFIG_INVALID',
|
|
87
|
+
debug: {
|
|
88
|
+
rootKeys: ['channel'],
|
|
89
|
+
channelConfigKeys: ['mqtt_topic'],
|
|
90
|
+
hasExistingMqttUrl: false
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -270,6 +270,10 @@ async function writeMem9Config(openclawRoot: string, apiKey: string): Promise<st
|
|
|
270
270
|
config.plugins.entries[MEM9_PLUGIN_ID] = {
|
|
271
271
|
...existingEntry,
|
|
272
272
|
enabled: true,
|
|
273
|
+
hooks: {
|
|
274
|
+
...(isRecord(existingEntry.hooks) ? existingEntry.hooks : {}),
|
|
275
|
+
allowConversationAccess: true
|
|
276
|
+
},
|
|
273
277
|
config: {
|
|
274
278
|
...existingPluginConfig,
|
|
275
279
|
apiUrl: MEM9_API_URL,
|
|
@@ -282,6 +286,7 @@ async function writeMem9Config(openclawRoot: string, apiKey: string): Promise<st
|
|
|
282
286
|
|
|
283
287
|
return [
|
|
284
288
|
hadExistingKey ? 'plugins.entries.mem9.config.apiKey (replaced)' : 'plugins.entries.mem9',
|
|
289
|
+
'plugins.entries.mem9.hooks.allowConversationAccess',
|
|
285
290
|
'plugins.slots.memory'
|
|
286
291
|
];
|
|
287
292
|
}
|
|
@@ -304,6 +309,10 @@ async function ensureMem9SlotConfig(openclawRoot: string, apiKey: string): Promi
|
|
|
304
309
|
config.plugins.entries[MEM9_PLUGIN_ID] = {
|
|
305
310
|
...existingEntry,
|
|
306
311
|
enabled: true,
|
|
312
|
+
hooks: {
|
|
313
|
+
...(isRecord(existingEntry.hooks) ? existingEntry.hooks : {}),
|
|
314
|
+
allowConversationAccess: true
|
|
315
|
+
},
|
|
307
316
|
config: {
|
|
308
317
|
...existingPluginConfig,
|
|
309
318
|
apiUrl: MEM9_API_URL,
|
|
@@ -316,6 +325,7 @@ async function ensureMem9SlotConfig(openclawRoot: string, apiKey: string): Promi
|
|
|
316
325
|
|
|
317
326
|
return [
|
|
318
327
|
'plugins.entries.mem9',
|
|
328
|
+
'plugins.entries.mem9.hooks.allowConversationAccess',
|
|
319
329
|
'plugins.slots.memory'
|
|
320
330
|
];
|
|
321
331
|
}
|
|
@@ -91,7 +91,7 @@ export const openclawUpdate: MethodHandler = async (_params, context: MethodCont
|
|
|
91
91
|
|
|
92
92
|
export const pluginSelfUpdate: MethodHandler = async (_params, context: MethodContext): Promise<JsonValue> => {
|
|
93
93
|
const result = await runOpenClawCommand(
|
|
94
|
-
['plugins', '
|
|
94
|
+
['plugins', 'update', 'rol-websocket-channel'],
|
|
95
95
|
context,
|
|
96
96
|
'pluginSelfUpdate'
|
|
97
97
|
);
|
|
@@ -99,7 +99,7 @@ export const pluginSelfUpdate: MethodHandler = async (_params, context: MethodCo
|
|
|
99
99
|
return {
|
|
100
100
|
ok: true,
|
|
101
101
|
action: 'pluginSelfUpdate',
|
|
102
|
-
plugin: '
|
|
102
|
+
plugin: 'rol-websocket-channel',
|
|
103
103
|
restartRecommended: true,
|
|
104
104
|
...result
|
|
105
105
|
};
|