rol-websocket-channel 1.6.3 → 1.6.7
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/index.js +6 -0
- package/dist/src/admin/lib/openclaw-bin.js +3 -0
- package/dist/src/admin/methods/mem9.js +32 -16
- package/index.ts +6 -0
- package/package.json +1 -1
- package/src/admin/lib/openclaw-bin.ts +3 -0
- package/src/admin/methods/mem9.ts +34 -18
package/dist/index.js
CHANGED
|
@@ -530,6 +530,12 @@ export async function handleCustomMessageType(msgType, innerData, traceId, accou
|
|
|
530
530
|
response.data = methodResult.result;
|
|
531
531
|
if (!methodResult.ok) {
|
|
532
532
|
response.error = methodResult.error?.message || "Unknown error";
|
|
533
|
+
if (methodResult.error?.code !== undefined) {
|
|
534
|
+
response.error_code = methodResult.error.code;
|
|
535
|
+
}
|
|
536
|
+
if (methodResult.error?.data !== undefined) {
|
|
537
|
+
response.error_data = methodResult.error.data;
|
|
538
|
+
}
|
|
533
539
|
if (isSkillInstallFlow) {
|
|
534
540
|
console.error(`[rol-websocket-channel] custom message failed: type=${msgType}, traceId=${traceId}, slug=${innerData?.slug ?? ""}, error=${response.error}, detail=${JSON.stringify(methodResult.error?.data ?? {})}`);
|
|
535
541
|
}
|
|
@@ -2,6 +2,7 @@ import { exec, execFile } from 'node:child_process';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
import { pathExists, readJsonFile, writeJsonFile } from '../lib/fs.js';
|
|
5
|
+
import { resolveOpenClawBin } from '../lib/openclaw-bin.js';
|
|
5
6
|
import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.js';
|
|
6
7
|
const execAsync = promisify(exec);
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
@@ -23,18 +24,12 @@ const RUNTIME_ENTRYPOINTS = [
|
|
|
23
24
|
'index.cjs'
|
|
24
25
|
];
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
26
|
-
// Resolve openclaw binary path (supports OPENCLAW_BIN env override)
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
function resolveOpenClawBin() {
|
|
29
|
-
return process.env.OPENCLAW_BIN || 'openclaw';
|
|
30
|
-
}
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
27
|
// Public API: installMem9 (idempotent, phase-based)
|
|
33
28
|
// ---------------------------------------------------------------------------
|
|
34
29
|
export async function installMem9(context) {
|
|
35
30
|
const config = await ensureOpenClawConfigExists(context.openclawRoot);
|
|
36
31
|
const currentState = readMem9State(config);
|
|
37
|
-
const currentEntrypoint = await findMem9RuntimeEntrypoint(context.openclawRoot);
|
|
32
|
+
const currentEntrypoint = await findMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
38
33
|
// Phase A: Plugin not installed → install only, then restart
|
|
39
34
|
if (!currentState.installed && !currentEntrypoint) {
|
|
40
35
|
await ensureOpenClawCli();
|
|
@@ -52,7 +47,7 @@ export async function installMem9(context) {
|
|
|
52
47
|
}
|
|
53
48
|
// Phase B: Installed but no key → create key, write config, restart
|
|
54
49
|
if (!currentState.configured || !currentState.apiKey) {
|
|
55
|
-
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot);
|
|
50
|
+
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
56
51
|
const apiKey = await createMem9Key();
|
|
57
52
|
const updated = await writeMem9Config(context.openclawRoot, apiKey);
|
|
58
53
|
const restart = await restartGateway(context.projectRoot);
|
|
@@ -73,7 +68,7 @@ export async function installMem9(context) {
|
|
|
73
68
|
};
|
|
74
69
|
}
|
|
75
70
|
// Phase C: Already configured → ensure slot/hooks/allow are correct
|
|
76
|
-
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot);
|
|
71
|
+
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
77
72
|
const updated = await ensureMem9SlotConfig(context.openclawRoot, currentState.apiKey);
|
|
78
73
|
const restart = await restartGateway(context.projectRoot);
|
|
79
74
|
return {
|
|
@@ -102,7 +97,7 @@ export async function reconnectMem9(key, context) {
|
|
|
102
97
|
}
|
|
103
98
|
const config = await ensureOpenClawConfigExists(context.openclawRoot);
|
|
104
99
|
const previousState = readMem9State(config);
|
|
105
|
-
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot);
|
|
100
|
+
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
106
101
|
const updated = await writeMem9Config(context.openclawRoot, apiKey);
|
|
107
102
|
const restart = await restartGateway(context.projectRoot);
|
|
108
103
|
return {
|
|
@@ -153,7 +148,7 @@ async function ensureOpenClawCli() {
|
|
|
153
148
|
await execFileAsync(bin, ['--version']);
|
|
154
149
|
}
|
|
155
150
|
catch (error) {
|
|
156
|
-
throw new JsonRpcException(JSON_RPC_ERRORS.internalError, `openclaw command is not available (tried: ${bin}).
|
|
151
|
+
throw new JsonRpcException(JSON_RPC_ERRORS.internalError, `openclaw command is not available (tried: ${bin}). Configure the OpenClaw binary path for the Gateway service.`, { code: 'MEM9_OPENCLAW_NOT_FOUND', bin, detail: error instanceof Error ? error.message : String(error) });
|
|
157
152
|
}
|
|
158
153
|
}
|
|
159
154
|
async function ensureNodeRuntime() {
|
|
@@ -178,8 +173,8 @@ async function installMem9Plugin(cwd) {
|
|
|
178
173
|
});
|
|
179
174
|
}
|
|
180
175
|
}
|
|
181
|
-
export async function findMem9RuntimeEntrypoint(openclawRoot) {
|
|
182
|
-
for (const packageRoot of
|
|
176
|
+
export async function findMem9RuntimeEntrypoint(openclawRoot, config) {
|
|
177
|
+
for (const packageRoot of resolveMem9RuntimePackageRoots(openclawRoot, config)) {
|
|
183
178
|
for (const entrypoint of RUNTIME_ENTRYPOINTS.map((item) => path.join(packageRoot, item))) {
|
|
184
179
|
if (await pathExists(entrypoint)) {
|
|
185
180
|
return entrypoint;
|
|
@@ -188,17 +183,34 @@ export async function findMem9RuntimeEntrypoint(openclawRoot) {
|
|
|
188
183
|
}
|
|
189
184
|
return null;
|
|
190
185
|
}
|
|
191
|
-
async function ensureMem9RuntimeEntrypoint(openclawRoot) {
|
|
192
|
-
const entrypoint = await findMem9RuntimeEntrypoint(openclawRoot);
|
|
186
|
+
async function ensureMem9RuntimeEntrypoint(openclawRoot, config) {
|
|
187
|
+
const entrypoint = await findMem9RuntimeEntrypoint(openclawRoot, config);
|
|
193
188
|
if (entrypoint) {
|
|
194
189
|
return entrypoint;
|
|
195
190
|
}
|
|
191
|
+
const installRecord = readMem9InstallRecord(config);
|
|
192
|
+
const checkedPackageRoots = resolveMem9RuntimePackageRoots(openclawRoot, config);
|
|
196
193
|
throw new JsonRpcException(JSON_RPC_ERRORS.internalError, 'mem9 plugin is installed but missing compiled runtime output', {
|
|
197
194
|
code: 'MEM9_RUNTIME_OUTPUT_MISSING',
|
|
198
195
|
expected: RUNTIME_ENTRYPOINTS.map((item) => `./${item.replace(/\\/g, '/')}`),
|
|
199
|
-
|
|
196
|
+
checkedPackageRoots,
|
|
197
|
+
checkedEntrypoints: checkedPackageRoots.flatMap((packageRoot) => RUNTIME_ENTRYPOINTS.map((item) => path.join(packageRoot, item))),
|
|
198
|
+
installRecord
|
|
200
199
|
});
|
|
201
200
|
}
|
|
201
|
+
function resolveMem9RuntimePackageRoots(openclawRoot, config) {
|
|
202
|
+
const roots = [];
|
|
203
|
+
const installPath = pickString(readMem9InstallRecord(config)?.installPath);
|
|
204
|
+
if (installPath) {
|
|
205
|
+
roots.push(path.isAbsolute(installPath) ? installPath : path.resolve(openclawRoot, installPath));
|
|
206
|
+
}
|
|
207
|
+
for (const fallbackRoot of MEM9_PACKAGE_ROOTS.map((item) => path.join(openclawRoot, item))) {
|
|
208
|
+
if (!roots.includes(fallbackRoot)) {
|
|
209
|
+
roots.push(fallbackRoot);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return roots;
|
|
213
|
+
}
|
|
202
214
|
async function createMem9Key() {
|
|
203
215
|
let response;
|
|
204
216
|
try {
|
|
@@ -368,6 +380,10 @@ function pickString(value) {
|
|
|
368
380
|
function isRecord(value) {
|
|
369
381
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
370
382
|
}
|
|
383
|
+
function readMem9InstallRecord(config) {
|
|
384
|
+
const record = config?.plugins?.installs?.[MEM9_PLUGIN_ID];
|
|
385
|
+
return isRecord(record) ? record : null;
|
|
386
|
+
}
|
|
371
387
|
function readMem9State(config) {
|
|
372
388
|
const installed = Boolean((config.plugins?.installs && typeof config.plugins.installs === 'object' && MEM9_PLUGIN_ID in config.plugins.installs)
|
|
373
389
|
|| (config.plugins?.entries && typeof config.plugins.entries === 'object' && MEM9_PLUGIN_ID in config.plugins.entries));
|
package/index.ts
CHANGED
|
@@ -681,6 +681,12 @@ export async function handleCustomMessageType(
|
|
|
681
681
|
response.data = methodResult.result;
|
|
682
682
|
if (!methodResult.ok) {
|
|
683
683
|
response.error = methodResult.error?.message || "Unknown error";
|
|
684
|
+
if (methodResult.error?.code !== undefined) {
|
|
685
|
+
response.error_code = methodResult.error.code;
|
|
686
|
+
}
|
|
687
|
+
if (methodResult.error?.data !== undefined) {
|
|
688
|
+
response.error_data = methodResult.error.data;
|
|
689
|
+
}
|
|
684
690
|
if (isSkillInstallFlow) {
|
|
685
691
|
console.error(
|
|
686
692
|
`[rol-websocket-channel] custom message failed: type=${msgType}, traceId=${traceId}, slug=${innerData?.slug ?? ""}, error=${response.error}, detail=${JSON.stringify(methodResult.error?.data ?? {})}`,
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
|
|
5
5
|
import { pathExists, readJsonFile, writeJsonFile } from '../lib/fs.js';
|
|
6
|
+
import { resolveOpenClawBin } from '../lib/openclaw-bin.js';
|
|
6
7
|
import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.js';
|
|
7
8
|
import type { JsonValue, MethodContext } from '../types.js';
|
|
8
9
|
|
|
@@ -38,14 +39,6 @@ interface OpenClawConfig {
|
|
|
38
39
|
[key: string]: any;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// Resolve openclaw binary path (supports OPENCLAW_BIN env override)
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
function resolveOpenClawBin(): string {
|
|
46
|
-
return process.env.OPENCLAW_BIN || 'openclaw';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
42
|
// ---------------------------------------------------------------------------
|
|
50
43
|
// Public API: installMem9 (idempotent, phase-based)
|
|
51
44
|
// ---------------------------------------------------------------------------
|
|
@@ -53,7 +46,7 @@ function resolveOpenClawBin(): string {
|
|
|
53
46
|
export async function installMem9(context: MethodContext): Promise<JsonValue> {
|
|
54
47
|
const config = await ensureOpenClawConfigExists(context.openclawRoot);
|
|
55
48
|
const currentState = readMem9State(config);
|
|
56
|
-
const currentEntrypoint = await findMem9RuntimeEntrypoint(context.openclawRoot);
|
|
49
|
+
const currentEntrypoint = await findMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
57
50
|
|
|
58
51
|
// Phase A: Plugin not installed → install only, then restart
|
|
59
52
|
if (!currentState.installed && !currentEntrypoint) {
|
|
@@ -74,7 +67,7 @@ export async function installMem9(context: MethodContext): Promise<JsonValue> {
|
|
|
74
67
|
|
|
75
68
|
// Phase B: Installed but no key → create key, write config, restart
|
|
76
69
|
if (!currentState.configured || !currentState.apiKey) {
|
|
77
|
-
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot);
|
|
70
|
+
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
78
71
|
const apiKey = await createMem9Key();
|
|
79
72
|
const updated = await writeMem9Config(context.openclawRoot, apiKey);
|
|
80
73
|
const restart = await restartGateway(context.projectRoot);
|
|
@@ -97,7 +90,7 @@ export async function installMem9(context: MethodContext): Promise<JsonValue> {
|
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
// Phase C: Already configured → ensure slot/hooks/allow are correct
|
|
100
|
-
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot);
|
|
93
|
+
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
101
94
|
const updated = await ensureMem9SlotConfig(context.openclawRoot, currentState.apiKey!);
|
|
102
95
|
const restart = await restartGateway(context.projectRoot);
|
|
103
96
|
|
|
@@ -130,7 +123,7 @@ export async function reconnectMem9(key: string, context: MethodContext): Promis
|
|
|
130
123
|
|
|
131
124
|
const config = await ensureOpenClawConfigExists(context.openclawRoot);
|
|
132
125
|
const previousState = readMem9State(config);
|
|
133
|
-
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot);
|
|
126
|
+
const runtimeEntrypoint = await ensureMem9RuntimeEntrypoint(context.openclawRoot, config);
|
|
134
127
|
const updated = await writeMem9Config(context.openclawRoot, apiKey);
|
|
135
128
|
const restart = await restartGateway(context.projectRoot);
|
|
136
129
|
|
|
@@ -189,7 +182,7 @@ async function ensureOpenClawCli(): Promise<void> {
|
|
|
189
182
|
} catch (error) {
|
|
190
183
|
throw new JsonRpcException(
|
|
191
184
|
JSON_RPC_ERRORS.internalError,
|
|
192
|
-
`openclaw command is not available (tried: ${bin}).
|
|
185
|
+
`openclaw command is not available (tried: ${bin}). Configure the OpenClaw binary path for the Gateway service.`,
|
|
193
186
|
{ code: 'MEM9_OPENCLAW_NOT_FOUND', bin, detail: error instanceof Error ? error.message : String(error) }
|
|
194
187
|
);
|
|
195
188
|
}
|
|
@@ -225,8 +218,8 @@ async function installMem9Plugin(cwd: string): Promise<void> {
|
|
|
225
218
|
}
|
|
226
219
|
}
|
|
227
220
|
|
|
228
|
-
export async function findMem9RuntimeEntrypoint(openclawRoot: string): Promise<string | null> {
|
|
229
|
-
for (const packageRoot of
|
|
221
|
+
export async function findMem9RuntimeEntrypoint(openclawRoot: string, config?: OpenClawConfig): Promise<string | null> {
|
|
222
|
+
for (const packageRoot of resolveMem9RuntimePackageRoots(openclawRoot, config)) {
|
|
230
223
|
for (const entrypoint of RUNTIME_ENTRYPOINTS.map((item) => path.join(packageRoot, item))) {
|
|
231
224
|
if (await pathExists(entrypoint)) {
|
|
232
225
|
return entrypoint;
|
|
@@ -236,22 +229,40 @@ export async function findMem9RuntimeEntrypoint(openclawRoot: string): Promise<s
|
|
|
236
229
|
return null;
|
|
237
230
|
}
|
|
238
231
|
|
|
239
|
-
async function ensureMem9RuntimeEntrypoint(openclawRoot: string): Promise<string> {
|
|
240
|
-
const entrypoint = await findMem9RuntimeEntrypoint(openclawRoot);
|
|
232
|
+
async function ensureMem9RuntimeEntrypoint(openclawRoot: string, config?: OpenClawConfig): Promise<string> {
|
|
233
|
+
const entrypoint = await findMem9RuntimeEntrypoint(openclawRoot, config);
|
|
241
234
|
if (entrypoint) {
|
|
242
235
|
return entrypoint;
|
|
243
236
|
}
|
|
237
|
+
const installRecord = readMem9InstallRecord(config);
|
|
238
|
+
const checkedPackageRoots = resolveMem9RuntimePackageRoots(openclawRoot, config);
|
|
244
239
|
throw new JsonRpcException(
|
|
245
240
|
JSON_RPC_ERRORS.internalError,
|
|
246
241
|
'mem9 plugin is installed but missing compiled runtime output',
|
|
247
242
|
{
|
|
248
243
|
code: 'MEM9_RUNTIME_OUTPUT_MISSING',
|
|
249
244
|
expected: RUNTIME_ENTRYPOINTS.map((item) => `./${item.replace(/\\/g, '/')}`),
|
|
250
|
-
|
|
245
|
+
checkedPackageRoots,
|
|
246
|
+
checkedEntrypoints: checkedPackageRoots.flatMap((packageRoot) => RUNTIME_ENTRYPOINTS.map((item) => path.join(packageRoot, item))),
|
|
247
|
+
installRecord
|
|
251
248
|
}
|
|
252
249
|
);
|
|
253
250
|
}
|
|
254
251
|
|
|
252
|
+
function resolveMem9RuntimePackageRoots(openclawRoot: string, config?: OpenClawConfig): string[] {
|
|
253
|
+
const roots: string[] = [];
|
|
254
|
+
const installPath = pickString(readMem9InstallRecord(config)?.installPath);
|
|
255
|
+
if (installPath) {
|
|
256
|
+
roots.push(path.isAbsolute(installPath) ? installPath : path.resolve(openclawRoot, installPath));
|
|
257
|
+
}
|
|
258
|
+
for (const fallbackRoot of MEM9_PACKAGE_ROOTS.map((item) => path.join(openclawRoot, item))) {
|
|
259
|
+
if (!roots.includes(fallbackRoot)) {
|
|
260
|
+
roots.push(fallbackRoot);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return roots;
|
|
264
|
+
}
|
|
265
|
+
|
|
255
266
|
async function createMem9Key(): Promise<string> {
|
|
256
267
|
let response: Response;
|
|
257
268
|
try {
|
|
@@ -447,6 +458,11 @@ function isRecord(value: unknown): value is Record<string, any> {
|
|
|
447
458
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
448
459
|
}
|
|
449
460
|
|
|
461
|
+
function readMem9InstallRecord(config?: OpenClawConfig): Record<string, any> | null {
|
|
462
|
+
const record = config?.plugins?.installs?.[MEM9_PLUGIN_ID];
|
|
463
|
+
return isRecord(record) ? record : null;
|
|
464
|
+
}
|
|
465
|
+
|
|
450
466
|
function readMem9State(config: OpenClawConfig): {
|
|
451
467
|
installed: boolean;
|
|
452
468
|
configured: boolean;
|