rol-websocket-channel 1.1.3 → 1.1.4

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/index.ts CHANGED
@@ -536,6 +536,7 @@ async function handleCustomMessageType(
536
536
  accountId: string,
537
537
  mqttTopic: string,
538
538
  ): Promise<void> {
539
+ const isSkillInstallFlow = msgType === "skillsInstallFromClawHub" || msgType === "skillsUpdateFromClawHub";
539
540
  const response: any = {
540
541
  type: "receiver",
541
542
  trace_id: traceId,
@@ -543,6 +544,12 @@ async function handleCustomMessageType(
543
544
  timestamp: Date.now(),
544
545
  };
545
546
 
547
+ if (isSkillInstallFlow) {
548
+ console.log(
549
+ `[rol-websocket-channel] custom message start: type=${msgType}, traceId=${traceId}, slug=${innerData?.slug ?? ""}, accountId=${accountId}, topic=${mqttTopic}`,
550
+ );
551
+ }
552
+
546
553
  const handlerMethod = (messageHandler as any)[msgType];
547
554
  if (typeof handlerMethod === "function") {
548
555
  try {
@@ -561,15 +568,34 @@ async function handleCustomMessageType(
561
568
  response.data = methodResult.result;
562
569
  if (!methodResult.ok) {
563
570
  response.error = methodResult.error?.message || "Unknown error";
571
+ if (isSkillInstallFlow) {
572
+ console.error(
573
+ `[rol-websocket-channel] custom message failed: type=${msgType}, traceId=${traceId}, slug=${innerData?.slug ?? ""}, error=${response.error}, detail=${JSON.stringify(methodResult.error?.data ?? {})}`,
574
+ );
575
+ }
576
+ } else if (isSkillInstallFlow) {
577
+ console.log(
578
+ `[rol-websocket-channel] custom message success: type=${msgType}, traceId=${traceId}, slug=${innerData?.slug ?? ""}`,
579
+ );
564
580
  }
565
581
  } else {
566
582
  // 旧格式:直接返回数据
567
583
  response.success = true;
568
584
  response.data = methodResult;
585
+ if (isSkillInstallFlow) {
586
+ console.log(
587
+ `[rol-websocket-channel] custom message success: type=${msgType}, traceId=${traceId}, slug=${innerData?.slug ?? ""}`,
588
+ );
589
+ }
569
590
  }
570
591
  } catch (handlerErr: any) {
571
592
  response.success = false;
572
593
  response.error = handlerErr.message;
594
+ if (isSkillInstallFlow) {
595
+ console.error(
596
+ `[rol-websocket-channel] custom message threw: type=${msgType}, traceId=${traceId}, slug=${innerData?.slug ?? ""}, error=${handlerErr?.message ?? String(handlerErr)}`,
597
+ );
598
+ }
573
599
  }
574
600
  } else {
575
601
  response.success = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rol-websocket-channel",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Unified OpenClaw plugin: MQTT Channel + Admin Bridge for remote management",
5
5
  "license": "MIT",
6
6
  "author": "nixgnehc",
@@ -21,6 +21,9 @@ export const getSession: MethodHandler = async (
21
21
  context
22
22
  ): Promise<JsonValue> => {
23
23
  const objectParams = isObject(params) ? params : {};
24
+ const agentId = typeof objectParams.agentId === 'string' && objectParams.agentId.trim()
25
+ ? objectParams.agentId.trim()
26
+ : undefined;
24
27
  const sessionId = typeof objectParams.sessionId === 'string' ? objectParams.sessionId : null;
25
28
  const requestedLimit = typeof objectParams.limit === 'number' ? objectParams.limit : MAX_SESSION_MESSAGES;
26
29
  const requestedOffset = typeof objectParams.offset === 'number' ? objectParams.offset : 0;
@@ -34,11 +37,11 @@ export const getSession: MethodHandler = async (
34
37
  );
35
38
  }
36
39
 
37
- const session = await findSessionRecord(context, sessionId);
40
+ const session = await findSessionRecord(context, sessionId, agentId);
38
41
  if (!session) {
39
42
  throw new JsonRpcException(
40
43
  JSON_RPC_ERRORS.invalidParams,
41
- `Session not found: ${sessionId}`
44
+ agentId ? `Session not found: ${sessionId} for agent ${agentId}` : `Session not found: ${sessionId}`
42
45
  );
43
46
  }
44
47
 
@@ -52,6 +55,7 @@ export const getSession: MethodHandler = async (
52
55
  const messages = await readSessionMessages(sessionFile, limit, offset);
53
56
 
54
57
  return {
58
+ agentId: session.agentId,
55
59
  agentName: session.agentName,
56
60
  sessionId: session.sessionId ?? sessionId,
57
61
  sessionKey: session.sessionKey,
@@ -76,6 +80,9 @@ export const prepareMessage: MethodHandler = async (
76
80
  context
77
81
  ): Promise<JsonValue> => {
78
82
  const objectParams = isObject(params) ? params : {};
83
+ const agentId = typeof objectParams.agentId === 'string' && objectParams.agentId.trim()
84
+ ? objectParams.agentId.trim()
85
+ : undefined;
79
86
  const sessionId = typeof objectParams.sessionId === 'string' ? objectParams.sessionId : null;
80
87
  const message = typeof objectParams.message === 'string' ? objectParams.message : null;
81
88
  const attachedSkills = Array.isArray(objectParams.attachedSkills) ? objectParams.attachedSkills : [];
@@ -94,15 +101,16 @@ export const prepareMessage: MethodHandler = async (
94
101
  );
95
102
  }
96
103
 
97
- const session = await findSessionRecord(context, sessionId);
104
+ const session = await findSessionRecord(context, sessionId, agentId);
98
105
  if (!session) {
99
106
  throw new JsonRpcException(
100
107
  JSON_RPC_ERRORS.invalidParams,
101
- `Session not found: ${sessionId}`
108
+ agentId ? `Session not found: ${sessionId} for agent ${agentId}` : `Session not found: ${sessionId}`
102
109
  );
103
110
  }
104
111
 
105
112
  const messageData: any = {
113
+ agentId: session.agentId,
106
114
  agentName: session.agentName,
107
115
  sessionId: session.sessionId ?? sessionId,
108
116
  sessionKey: session.sessionKey,
@@ -26,6 +26,7 @@ interface SessionsIndexEntry {
26
26
  type SessionsIndex = Record<string, SessionsIndexEntry>;
27
27
 
28
28
  export interface AgentSessionRecord {
29
+ agentId: string;
29
30
  agentName: string;
30
31
  sessionKey: string;
31
32
  sessionId: string | null;
@@ -41,17 +42,24 @@ export interface AgentSessionRecord {
41
42
  }
42
43
 
43
44
  export const listSessions: MethodHandler = async (
44
- _params,
45
+ params,
45
46
  context
46
47
  ): Promise<JsonValue> => {
47
- const items = await listAllAgentSessions(context);
48
+ const objectParams = isObject(params) ? params : {};
49
+ const agentId = typeof objectParams.agentId === 'string' && objectParams.agentId.trim()
50
+ ? objectParams.agentId.trim()
51
+ : undefined;
52
+ const items = await listAllAgentSessions(context, agentId);
48
53
  return {
49
54
  count: items.length,
50
55
  items
51
56
  };
52
57
  };
53
58
 
54
- export async function listAllAgentSessions(context: MethodContext): Promise<AgentSessionRecord[]> {
59
+ export async function listAllAgentSessions(
60
+ context: MethodContext,
61
+ agentId?: string
62
+ ): Promise<AgentSessionRecord[]> {
55
63
  const agentsRoot = path.join(context.openclawRoot, 'agents');
56
64
  if (!(await pathExists(agentsRoot))) {
57
65
  return [];
@@ -66,6 +74,10 @@ export async function listAllAgentSessions(context: MethodContext): Promise<Agen
66
74
  }
67
75
 
68
76
  const agentName = agentEntry.name;
77
+ if (agentId && agentName !== agentId) {
78
+ continue;
79
+ }
80
+
69
81
  const sessionsPath = path.join(agentsRoot, agentName, 'sessions', 'sessions.json');
70
82
  if (!(await pathExists(sessionsPath))) {
71
83
  continue;
@@ -75,6 +87,7 @@ export async function listAllAgentSessions(context: MethodContext): Promise<Agen
75
87
  for (const [sessionKey, entry] of Object.entries(sessions)) {
76
88
  const sessionId = entry.sessionId ?? extractSessionIdFromFile(entry.sessionFile) ?? null;
77
89
  items.push({
90
+ agentId: agentName,
78
91
  agentName,
79
92
  sessionKey,
80
93
  sessionId,
@@ -98,20 +111,26 @@ export async function listAllAgentSessions(context: MethodContext): Promise<Agen
98
111
 
99
112
  export async function findSessionRecord(
100
113
  context: MethodContext,
101
- sessionIdOrKey: string
114
+ sessionIdOrKey: string,
115
+ agentId?: string
102
116
  ): Promise<AgentSessionRecord | null> {
103
- const sessions = await listAllAgentSessions(context);
117
+ const sessions = await listAllAgentSessions(context, agentId);
104
118
  return sessions.find((item) => item.sessionId === sessionIdOrKey || item.sessionKey === sessionIdOrKey) ?? null;
105
119
  }
106
120
 
107
121
  export async function resolveSessionFile(
108
122
  context: MethodContext,
109
- sessionIdOrKey: string
123
+ sessionIdOrKey: string,
124
+ agentId?: string
110
125
  ): Promise<string | null> {
111
- const session = await findSessionRecord(context, sessionIdOrKey);
126
+ const session = await findSessionRecord(context, sessionIdOrKey, agentId);
112
127
  return session?.sessionFilePath ?? null;
113
128
  }
114
129
 
130
+ function isObject(value: JsonValue | undefined): value is Record<string, JsonValue> {
131
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
132
+ }
133
+
115
134
  function extractSessionIdFromFile(sessionFile: string | undefined): string | null {
116
135
  if (!sessionFile) {
117
136
  return null;
@@ -143,7 +143,7 @@ export const searchClawHubSkills: MethodHandler = async (params, context): Promi
143
143
  const objectParams = isObject(params) ? params : {};
144
144
  const query = typeof objectParams.query === 'string' ? objectParams.query.trim() : '';
145
145
  const args = query ? ['skills', 'search', query, '--json'] : ['skills', 'search', '--json'];
146
- const result = await runOpenClawSkillCommand(args, context.projectRoot);
146
+ const result = await runOpenClawSkillCommand(args, context.openclawRoot, context.openclawRoot);
147
147
 
148
148
  return {
149
149
  ok: true,
@@ -155,7 +155,7 @@ export const searchClawHubSkills: MethodHandler = async (params, context): Promi
155
155
  export const installSkillFromClawHub: MethodHandler = async (params, context): Promise<JsonValue> => {
156
156
  const objectParams = expectObject(params) as unknown as ClawHubSkillParams;
157
157
  const slug = expectString(objectParams.slug, 'slug');
158
- const result = await runOpenClawSkillCommand(['skills', 'install', slug], context.projectRoot);
158
+ const result = await runOpenClawSkillCommand(['skills', 'install', slug], context.openclawRoot, context.openclawRoot);
159
159
 
160
160
  return {
161
161
  ok: true,
@@ -167,7 +167,7 @@ export const installSkillFromClawHub: MethodHandler = async (params, context): P
167
167
  export const updateSkillFromClawHub: MethodHandler = async (params, context): Promise<JsonValue> => {
168
168
  const objectParams = expectObject(params) as unknown as ClawHubSkillParams;
169
169
  const slug = expectString(objectParams.slug, 'slug');
170
- const result = await runOpenClawSkillCommand(['skills', 'update', slug], context.projectRoot);
170
+ const result = await runOpenClawSkillCommand(['skills', 'update', slug], context.openclawRoot, context.openclawRoot);
171
171
 
172
172
  return {
173
173
  ok: true,
@@ -233,27 +233,42 @@ async function npmPack(packageSpec: string, cwd: string): Promise<string> {
233
233
 
234
234
  async function runOpenClawSkillCommand(
235
235
  args: string[],
236
- cwd: string
236
+ cwd: string,
237
+ openclawRoot?: string
237
238
  ): Promise<{ stdout: string; stderr: string; parsed: JsonValue | null }> {
238
239
  const command = process.env.OPENCLAW_BIN || 'openclaw';
239
- const options = buildOpenClawExecOptions(cwd);
240
+ const options = buildOpenClawExecOptions(cwd, openclawRoot);
241
+ const openclawBin = process.env.OPENCLAW_BIN || '';
242
+ const openclawHome = options.env?.OPENCLAW_HOME || '';
243
+
244
+ console.log(
245
+ `[skills] exec start: command=${command}, args=${JSON.stringify(args)}, cwd=${cwd}, OPENCLAW_BIN=${openclawBin}, OPENCLAW_HOME=${openclawHome}`
246
+ );
240
247
 
241
248
  try {
242
249
  const { stdout, stderr } = await execFileAsync(command, args, options);
250
+ console.log(
251
+ `[skills] exec success: command=${command}, args=${JSON.stringify(args)}, stdoutLength=${stdout.length}, stderrLength=${stderr.length}`
252
+ );
243
253
  return {
244
254
  stdout,
245
255
  stderr,
246
256
  parsed: parseJsonOutput(stdout)
247
257
  };
248
258
  } catch (err: any) {
259
+ const stdout = typeof err?.stdout === 'string' ? err.stdout : '';
260
+ const stderr = typeof err?.stderr === 'string' ? err.stderr : '';
261
+ console.error(
262
+ `[skills] exec failed: command=${command}, args=${JSON.stringify(args)}, cwd=${cwd}, OPENCLAW_BIN=${openclawBin}, OPENCLAW_HOME=${openclawHome}, stdout=${JSON.stringify(stdout)}, stderr=${JSON.stringify(stderr)}`
263
+ );
249
264
  throw new JsonRpcException(
250
265
  JSON_RPC_ERRORS.internalError,
251
266
  `OpenClaw skill command failed: ${err instanceof Error ? err.message : String(err)}`,
252
267
  {
253
268
  command,
254
269
  args,
255
- stdout: typeof err?.stdout === 'string' ? err.stdout : '',
256
- stderr: typeof err?.stderr === 'string' ? err.stderr : ''
270
+ stdout,
271
+ stderr
257
272
  }
258
273
  );
259
274
  }
@@ -274,7 +289,7 @@ function parseJsonOutput(stdout: string): JsonValue | null {
274
289
 
275
290
  async function queryOpenClawSkills(context: MethodContext): Promise<unknown[]> {
276
291
  const command = process.env.OPENCLAW_BIN || 'openclaw';
277
- const options = buildOpenClawExecOptions(context.projectRoot);
292
+ const options = buildOpenClawExecOptions(context.openclawRoot, context.openclawRoot);
278
293
 
279
294
  try {
280
295
  const { stdout } = await execFileAsync(command, ['skills', 'list', '--json'], options);
@@ -300,15 +315,38 @@ async function queryOpenClawSkills(context: MethodContext): Promise<unknown[]> {
300
315
  }
301
316
  }
302
317
 
303
- function buildOpenClawExecOptions(cwd: string): { cwd: string; shell?: boolean } {
318
+ function buildOpenClawExecOptions(
319
+ cwd: string,
320
+ openclawRoot?: string
321
+ ): { cwd: string; shell?: boolean; env: NodeJS.ProcessEnv } {
322
+ const env: NodeJS.ProcessEnv = { ...process.env };
323
+ const openclawHome = resolveOpenClawHomeForCli(openclawRoot);
324
+ if (openclawHome) {
325
+ env.OPENCLAW_HOME = openclawHome;
326
+ }
327
+
304
328
  if (process.platform === 'win32') {
305
329
  return {
306
330
  cwd,
307
- shell: true
331
+ shell: true,
332
+ env
308
333
  };
309
334
  }
310
335
 
311
- return { cwd };
336
+ return { cwd, env };
337
+ }
338
+
339
+ function resolveOpenClawHomeForCli(openclawRoot?: string): string | undefined {
340
+ if (!openclawRoot) {
341
+ return process.env.OPENCLAW_HOME;
342
+ }
343
+
344
+ const resolvedRoot = path.resolve(openclawRoot);
345
+ if (path.basename(resolvedRoot) === '.openclaw') {
346
+ return path.dirname(resolvedRoot);
347
+ }
348
+
349
+ return process.env.OPENCLAW_HOME;
312
350
  }
313
351
 
314
352
  function normalizeCliSkill(