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 +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 +49 -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,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
|
|
256
|
-
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.
|
|
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(
|
|
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(
|