rol-websocket-channel 1.0.0
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.md +967 -0
- package/dist/index.js +430 -0
- package/dist/message-handler.js +327 -0
- package/dist/src/admin/cli.js +43 -0
- package/dist/src/admin/jsonrpc.js +60 -0
- package/dist/src/admin/lib/fs.js +30 -0
- package/dist/src/admin/lib/paths.js +46 -0
- package/dist/src/admin/methods/admin.js +60 -0
- package/dist/src/admin/methods/agents-extended.js +235 -0
- package/dist/src/admin/methods/index.js +69 -0
- package/dist/src/admin/methods/memory.js +360 -0
- package/dist/src/admin/methods/models-extended.js +107 -0
- package/dist/src/admin/methods/models.js +39 -0
- package/dist/src/admin/methods/sessions-extended.js +207 -0
- package/dist/src/admin/methods/sessions.js +64 -0
- package/dist/src/admin/methods/skills-extended.js +157 -0
- package/dist/src/admin/methods/skills-toggle.js +182 -0
- package/dist/src/admin/methods/skills.js +384 -0
- package/dist/src/admin/methods/system.js +178 -0
- package/dist/src/admin/methods/usage.js +1170 -0
- package/dist/src/admin/types.js +1 -0
- package/dist/src/mqtt/connection-manager.js +155 -0
- package/dist/src/mqtt/index.js +5 -0
- package/dist/src/mqtt/mqtt-client.js +86 -0
- package/dist/src/mqtt/types.js +2 -0
- package/dist/src/shared/context.js +24 -0
- package/dist/src/shared/wrapper.js +23 -0
- package/index.ts +514 -0
- package/message-handler.ts +415 -0
- package/openclaw.plugin.json +84 -0
- package/package.json +35 -0
- package/readme.md +32 -0
- package/src/admin/cli.ts +60 -0
- package/src/admin/jsonrpc.ts +88 -0
- package/src/admin/lib/fs.ts +35 -0
- package/src/admin/lib/paths.ts +61 -0
- package/src/admin/methods/admin.ts +95 -0
- package/src/admin/methods/agents-extended.ts +310 -0
- package/src/admin/methods/index.ts +103 -0
- package/src/admin/methods/memory.ts +546 -0
- package/src/admin/methods/models-extended.ts +191 -0
- package/src/admin/methods/models.ts +103 -0
- package/src/admin/methods/sessions-extended.ts +313 -0
- package/src/admin/methods/sessions.ts +122 -0
- package/src/admin/methods/skills-extended.ts +249 -0
- package/src/admin/methods/skills-toggle.ts +235 -0
- package/src/admin/methods/skills.ts +651 -0
- package/src/admin/methods/system.ts +203 -0
- package/src/admin/methods/usage.ts +1491 -0
- package/src/admin/types.ts +46 -0
- package/src/mqtt/connection-manager.ts +188 -0
- package/src/mqtt/index.ts +6 -0
- package/src/mqtt/mqtt-client.ts +119 -0
- package/src/mqtt/types.ts +36 -0
- package/src/shared/context.ts +33 -0
- package/src/shared/wrapper.ts +35 -0
- package/tsconfig.json +16 -0
- package/types/openclaw.d.ts +74 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import type { JsonValue, MethodHandler, MethodContext } from '../types.ts';
|
|
6
|
+
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
|
|
9
|
+
export const ping: MethodHandler = async (): Promise<JsonValue> => {
|
|
10
|
+
return {
|
|
11
|
+
ok: true,
|
|
12
|
+
service: 'openclaw-plugin-bridge',
|
|
13
|
+
version: '0.1.0'
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const restart: MethodHandler = async (_params, context: MethodContext): Promise<JsonValue> => {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout, stderr } = await execAsync('openclaw gateway restart', {
|
|
20
|
+
cwd: context.openclawRoot
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
ok: true,
|
|
25
|
+
message: 'OpenClaw gateway restart command executed',
|
|
26
|
+
stdout: stdout.trim(),
|
|
27
|
+
stderr: stderr.trim()
|
|
28
|
+
};
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
error: error instanceof Error ? error.message : String(error)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const stop: MethodHandler = async (_params, context: MethodContext): Promise<JsonValue> => {
|
|
38
|
+
try {
|
|
39
|
+
const { stdout, stderr } = await execAsync('openclaw gateway stop', {
|
|
40
|
+
cwd: context.openclawRoot
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
ok: true,
|
|
45
|
+
message: 'OpenClaw gateway stop command executed',
|
|
46
|
+
stdout: stdout.trim(),
|
|
47
|
+
stderr: stderr.trim()
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
error: error instanceof Error ? error.message : String(error)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const doctorFix: MethodHandler = async (_params, context: MethodContext): Promise<JsonValue> => {
|
|
58
|
+
try {
|
|
59
|
+
const { stdout, stderr } = await execAsync('openclaw doctor --fix', {
|
|
60
|
+
cwd: context.openclawRoot
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
ok: true,
|
|
65
|
+
message: 'OpenClaw doctor --fix executed',
|
|
66
|
+
stdout: stdout.trim(),
|
|
67
|
+
stderr: stderr.trim()
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: error instanceof Error ? error.message : String(error)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const logs: MethodHandler = async (params: any, context: MethodContext): Promise<JsonValue> => {
|
|
78
|
+
try {
|
|
79
|
+
// 根据实际情况可能需要调整,这里默认尝试 project root 或 user home 下的 .openclaw/logs
|
|
80
|
+
// 很多时候全局日志位于 ~/.openclaw/logs/gateway.log 或工程目录下的 .openclaw 文件夹中
|
|
81
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
82
|
+
|
|
83
|
+
// 优先级列表:1. 系统的 /tmp/openclaw (Linux/WSL 常见) 2. 用户目录 3. 项目目录
|
|
84
|
+
const candidateDirs = [
|
|
85
|
+
'/tmp/openclaw',
|
|
86
|
+
path.join(homeDir, '.openclaw', 'logs'),
|
|
87
|
+
path.join(context.openclawRoot, 'logs'),
|
|
88
|
+
path.join(context.openclawRoot, '.openclaw', 'logs')
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
let logDir = '';
|
|
92
|
+
let allLogFiles: string[] = [];
|
|
93
|
+
|
|
94
|
+
// 寻找第一个存在且包含 .log 文件的目录
|
|
95
|
+
for (const dir of candidateDirs) {
|
|
96
|
+
try {
|
|
97
|
+
const files = await fs.readdir(dir);
|
|
98
|
+
const logs = files.filter(f => f.endsWith('.log'));
|
|
99
|
+
if (logs.length > 0) {
|
|
100
|
+
logDir = dir;
|
|
101
|
+
allLogFiles = logs;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!logDir) {
|
|
110
|
+
return { ok: false, error: `No log directory found in candidates: ${candidateDirs.join(', ')}` };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 优先尝试找 openclaw-*.log (这是最正宗的系统日志)
|
|
114
|
+
const openclawPatternFiles = allLogFiles.filter(f => f.startsWith('openclaw-') && f.endsWith('.log'));
|
|
115
|
+
|
|
116
|
+
// 如果没有这种格式,再退而求其次找包含 gateway 的
|
|
117
|
+
const coreLogFiles = allLogFiles.filter(f => f.includes('gateway'));
|
|
118
|
+
|
|
119
|
+
// 最终选定的候选集合:优先 openclaw-*, 其次 gateway, 实在没有就全选(包含 fallback)
|
|
120
|
+
let logFiles = openclawPatternFiles.length > 0
|
|
121
|
+
? openclawPatternFiles
|
|
122
|
+
: (coreLogFiles.length > 0 ? coreLogFiles : allLogFiles);
|
|
123
|
+
|
|
124
|
+
// 排除明确的干扰项(除非当前集合只剩它们了)
|
|
125
|
+
if (logFiles.length > 1) {
|
|
126
|
+
const filtered = logFiles.filter(f =>
|
|
127
|
+
!f.includes('events') &&
|
|
128
|
+
!f.includes('telemetry') &&
|
|
129
|
+
!f.includes('sessions') &&
|
|
130
|
+
!f.includes('webchat')
|
|
131
|
+
);
|
|
132
|
+
if (filtered.length > 0) logFiles = filtered;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (logFiles.length === 0) {
|
|
136
|
+
return { ok: false, error: `No .log files found in directory: ${logDir}` };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const limit = params?.limit ?? 200;
|
|
140
|
+
const maxBytes = params?.maxBytes ?? 250000;
|
|
141
|
+
let offset = params?.offset;
|
|
142
|
+
|
|
143
|
+
// 获取所有候选日志的详细信息并排序(对应 ls -t)
|
|
144
|
+
const fileStats = await Promise.all(
|
|
145
|
+
logFiles.map(async (file) => {
|
|
146
|
+
const filePath = path.join(logDir, file);
|
|
147
|
+
const s = await fs.stat(filePath);
|
|
148
|
+
return { file, filePath, mtime: s.mtimeMs, size: s.size };
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// 按照修改时间降序(最新的在最前面)
|
|
153
|
+
fileStats.sort((a, b) => b.mtime - a.mtime);
|
|
154
|
+
|
|
155
|
+
const targetLogFile = fileStats[0].filePath;
|
|
156
|
+
const size = fileStats[0].size;
|
|
157
|
+
|
|
158
|
+
let readSize = 0;
|
|
159
|
+
let startOffset = 0;
|
|
160
|
+
|
|
161
|
+
if (typeof offset === 'number') {
|
|
162
|
+
if (offset > size) offset = 0; // 如果文件变小(可能是被 rotate 从头开始了)
|
|
163
|
+
startOffset = offset;
|
|
164
|
+
readSize = Math.min(size - startOffset, maxBytes);
|
|
165
|
+
} else {
|
|
166
|
+
// 首次加载,没有携带 offset;只拿尾部数据
|
|
167
|
+
readSize = Math.min(size, maxBytes);
|
|
168
|
+
startOffset = size - readSize;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (readSize <= 0) {
|
|
172
|
+
return { ok: true, lines: [], nextOffset: size };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const fd = await fs.open(targetLogFile, 'r');
|
|
176
|
+
const buffer = Buffer.alloc(readSize);
|
|
177
|
+
await fd.read(buffer, 0, readSize, startOffset);
|
|
178
|
+
await fd.close();
|
|
179
|
+
|
|
180
|
+
const content = buffer.toString('utf-8');
|
|
181
|
+
const rawLines = content.split(/\r?\n/).filter(line => line.trim().length > 0);
|
|
182
|
+
|
|
183
|
+
const resultLines = typeof offset === 'number' ? rawLines : rawLines.slice(-limit);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
ok: true,
|
|
187
|
+
nextOffset: startOffset + readSize,
|
|
188
|
+
lines: resultLines.map(line => {
|
|
189
|
+
try {
|
|
190
|
+
// OpenClaw 日志已经是 JSON lines
|
|
191
|
+
return JSON.parse(line);
|
|
192
|
+
} catch {
|
|
193
|
+
return line;
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
};
|
|
197
|
+
} catch (error) {
|
|
198
|
+
return {
|
|
199
|
+
ok: false,
|
|
200
|
+
error: error instanceof Error ? error.message : String(error)
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
};
|