rol-websocket-channel 1.1.3 → 1.1.5
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 +26 -0
- package/package.json +1 -1
- package/src/admin/methods/sessions-extended.ts +12 -4
- package/src/admin/methods/sessions.ts +26 -7
- package/src/admin/methods/skills.ts +57 -11
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
|
@@ -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
|
-
|
|
45
|
+
params,
|
|
45
46
|
context
|
|
46
47
|
): Promise<JsonValue> => {
|
|
47
|
-
const
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
170
|
+
const result = await runOpenClawSkillCommand(['skills', 'update', slug], context.openclawRoot, context.openclawRoot);
|
|
171
171
|
|
|
172
172
|
return {
|
|
173
173
|
ok: true,
|
|
@@ -233,27 +233,43 @@ 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
|
+
const home = options.env?.HOME || '';
|
|
244
|
+
|
|
245
|
+
console.log(
|
|
246
|
+
`[skills] exec start: command=${command}, args=${JSON.stringify(args)}, cwd=${cwd}, OPENCLAW_BIN=${openclawBin}, OPENCLAW_HOME=${openclawHome}, HOME=${home}`
|
|
247
|
+
);
|
|
240
248
|
|
|
241
249
|
try {
|
|
242
250
|
const { stdout, stderr } = await execFileAsync(command, args, options);
|
|
251
|
+
console.log(
|
|
252
|
+
`[skills] exec success: command=${command}, args=${JSON.stringify(args)}, stdoutLength=${stdout.length}, stderrLength=${stderr.length}`
|
|
253
|
+
);
|
|
243
254
|
return {
|
|
244
255
|
stdout,
|
|
245
256
|
stderr,
|
|
246
257
|
parsed: parseJsonOutput(stdout)
|
|
247
258
|
};
|
|
248
259
|
} catch (err: any) {
|
|
260
|
+
const stdout = typeof err?.stdout === 'string' ? err.stdout : '';
|
|
261
|
+
const stderr = typeof err?.stderr === 'string' ? err.stderr : '';
|
|
262
|
+
console.error(
|
|
263
|
+
`[skills] exec failed: command=${command}, args=${JSON.stringify(args)}, cwd=${cwd}, OPENCLAW_BIN=${openclawBin}, OPENCLAW_HOME=${openclawHome}, HOME=${home}, stdout=${JSON.stringify(stdout)}, stderr=${JSON.stringify(stderr)}`
|
|
264
|
+
);
|
|
249
265
|
throw new JsonRpcException(
|
|
250
266
|
JSON_RPC_ERRORS.internalError,
|
|
251
267
|
`OpenClaw skill command failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
252
268
|
{
|
|
253
269
|
command,
|
|
254
270
|
args,
|
|
255
|
-
stdout
|
|
256
|
-
stderr
|
|
271
|
+
stdout,
|
|
272
|
+
stderr
|
|
257
273
|
}
|
|
258
274
|
);
|
|
259
275
|
}
|
|
@@ -274,7 +290,7 @@ function parseJsonOutput(stdout: string): JsonValue | null {
|
|
|
274
290
|
|
|
275
291
|
async function queryOpenClawSkills(context: MethodContext): Promise<unknown[]> {
|
|
276
292
|
const command = process.env.OPENCLAW_BIN || 'openclaw';
|
|
277
|
-
const options = buildOpenClawExecOptions(context.
|
|
293
|
+
const options = buildOpenClawExecOptions(context.openclawRoot, context.openclawRoot);
|
|
278
294
|
|
|
279
295
|
try {
|
|
280
296
|
const { stdout } = await execFileAsync(command, ['skills', 'list', '--json'], options);
|
|
@@ -300,15 +316,45 @@ async function queryOpenClawSkills(context: MethodContext): Promise<unknown[]> {
|
|
|
300
316
|
}
|
|
301
317
|
}
|
|
302
318
|
|
|
303
|
-
function buildOpenClawExecOptions(
|
|
319
|
+
function buildOpenClawExecOptions(
|
|
320
|
+
cwd: string,
|
|
321
|
+
openclawRoot?: string
|
|
322
|
+
): { cwd: string; shell?: boolean; env: NodeJS.ProcessEnv } {
|
|
323
|
+
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
324
|
+
const openclawHome = resolveOpenClawHomeForCli(openclawRoot);
|
|
325
|
+
if (openclawHome) {
|
|
326
|
+
env.OPENCLAW_HOME = openclawHome;
|
|
327
|
+
}
|
|
328
|
+
if (openclawRoot && path.basename(path.resolve(openclawRoot)) === '.openclaw') {
|
|
329
|
+
const home = path.dirname(path.resolve(openclawRoot));
|
|
330
|
+
env.HOME = home;
|
|
331
|
+
if (process.platform === 'win32') {
|
|
332
|
+
env.USERPROFILE = home;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
304
336
|
if (process.platform === 'win32') {
|
|
305
337
|
return {
|
|
306
338
|
cwd,
|
|
307
|
-
shell: true
|
|
339
|
+
shell: true,
|
|
340
|
+
env
|
|
308
341
|
};
|
|
309
342
|
}
|
|
310
343
|
|
|
311
|
-
return { cwd };
|
|
344
|
+
return { cwd, env };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function resolveOpenClawHomeForCli(openclawRoot?: string): string | undefined {
|
|
348
|
+
if (!openclawRoot) {
|
|
349
|
+
return process.env.OPENCLAW_HOME;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const resolvedRoot = path.resolve(openclawRoot);
|
|
353
|
+
if (path.basename(resolvedRoot) === '.openclaw') {
|
|
354
|
+
return path.dirname(resolvedRoot);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return process.env.OPENCLAW_HOME;
|
|
312
358
|
}
|
|
313
359
|
|
|
314
360
|
function normalizeCliSkill(
|