@newbase-clawchat/openclaw-clawchat 2026.4.24 → 2026.4.30
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/README.md +66 -16
- package/dist/index.js +27 -0
- package/dist/src/api-client.js +156 -0
- package/dist/src/api-types.js +17 -0
- package/dist/src/buffered-stream.js +177 -0
- package/dist/src/channel.js +191 -0
- package/dist/src/client.js +176 -0
- package/dist/src/commands.js +35 -0
- package/dist/src/config.js +214 -0
- package/dist/src/inbound.js +133 -0
- package/dist/src/login.runtime.js +130 -0
- package/dist/src/media-runtime.js +85 -0
- package/dist/src/message-mapper.js +82 -0
- package/dist/src/outbound.js +181 -0
- package/dist/src/protocol.js +38 -0
- package/dist/src/reply-dispatcher.js +440 -0
- package/dist/src/runtime.js +288 -0
- package/dist/src/streaming.js +65 -0
- package/dist/src/tools-schema.js +38 -0
- package/dist/src/tools.js +287 -0
- package/index.ts +2 -1
- package/openclaw.plugin.json +81 -1
- package/package.json +21 -9
- package/skills/clawchat-account-tools/SKILL.md +26 -0
- package/skills/clawchat-activate/SKILL.md +47 -0
- package/src/api-client.test.ts +6 -5
- package/src/api-client.ts +8 -3
- package/src/buffered-stream.test.ts +14 -4
- package/src/buffered-stream.ts +19 -11
- package/src/channel.outbound.test.ts +49 -35
- package/src/channel.test.ts +45 -10
- package/src/channel.ts +26 -17
- package/src/client.test.ts +9 -1
- package/src/client.ts +48 -21
- package/src/commands.test.ts +39 -0
- package/src/commands.ts +41 -0
- package/src/config.test.ts +40 -3
- package/src/config.ts +60 -4
- package/src/inbound.test.ts +9 -6
- package/src/inbound.ts +51 -16
- package/src/login.runtime.test.ts +142 -3
- package/src/login.runtime.ts +59 -26
- package/src/manifest.test.ts +183 -5
- package/src/outbound.test.ts +10 -7
- package/src/outbound.ts +8 -7
- package/src/plugin-entry.test.ts +27 -0
- package/src/protocol.ts +5 -0
- package/src/reply-dispatcher.test.ts +420 -3
- package/src/reply-dispatcher.ts +137 -12
- package/src/runtime.test.ts +23 -7
- package/src/runtime.ts +13 -1
- package/src/streaming.test.ts +12 -9
- package/src/streaming.ts +22 -12
- package/src/tools-schema.ts +28 -19
- package/src/tools.test.ts +181 -40
- package/src/tools.ts +107 -95
package/src/tools.ts
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import type {
|
|
4
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/
|
|
3
|
+
import type { OpenClawAgentToolResult } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
4
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
|
5
|
+
import type { OpenclawClawchatMutateConfigFile } from "./login.runtime.ts";
|
|
5
6
|
import { createOpenclawClawlingApiClient } from "./api-client.ts";
|
|
6
7
|
import { ClawlingApiError, type Profile } from "./api-types.ts";
|
|
7
8
|
import { resolveOpenclawClawlingAccount } from "./config.ts";
|
|
8
9
|
import {
|
|
9
10
|
ClawchatActivateSchema,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
ClawchatGetAccountProfileSchema,
|
|
12
|
+
ClawchatGetUserProfileSchema,
|
|
13
|
+
ClawchatListAccountFriendsSchema,
|
|
14
|
+
ClawchatUpdateAccountProfileSchema,
|
|
15
|
+
ClawchatUploadAvatarImageSchema,
|
|
16
|
+
ClawchatUploadMediaFileSchema,
|
|
16
17
|
type ClawchatActivateParams,
|
|
17
|
-
type
|
|
18
|
-
type
|
|
19
|
-
type
|
|
20
|
-
type
|
|
21
|
-
type
|
|
18
|
+
type ClawchatGetUserProfileParams,
|
|
19
|
+
type ClawchatListAccountFriendsParams,
|
|
20
|
+
type ClawchatUpdateAccountProfileParams,
|
|
21
|
+
type ClawchatUploadAvatarImageParams,
|
|
22
|
+
type ClawchatUploadMediaFileParams,
|
|
22
23
|
} from "./tools-schema.ts";
|
|
23
24
|
|
|
24
25
|
const MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
|
|
25
26
|
|
|
26
|
-
function jsonResponse(data: unknown):
|
|
27
|
+
function jsonResponse(data: unknown): OpenClawAgentToolResult<unknown> {
|
|
27
28
|
return {
|
|
28
29
|
content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
|
|
29
30
|
details: data,
|
|
@@ -46,6 +47,13 @@ function validationError(message: string) {
|
|
|
46
47
|
return jsonResponse({ error: "validation", message });
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
function resolveActivateCode(params: ClawchatActivateParams & { command?: unknown }): string {
|
|
51
|
+
const explicit = typeof params.code === "string" ? params.code.trim() : "";
|
|
52
|
+
if (explicit) return explicit;
|
|
53
|
+
const command = typeof params.command === "string" ? params.command.trim() : "";
|
|
54
|
+
return command.match(/\b[A-Z0-9]{6}\b/u)?.[0] ?? "";
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
function genericError(err: unknown) {
|
|
50
58
|
return jsonResponse({
|
|
51
59
|
error: "unknown",
|
|
@@ -87,31 +95,27 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
87
95
|
return;
|
|
88
96
|
}
|
|
89
97
|
|
|
90
|
-
// -----------------------------------------------------------------------
|
|
91
|
-
// `clawchat_activate` is registered UNCONDITIONALLY — it is the tool the
|
|
92
|
-
// agent invokes to onboard this plugin, so it must be available even when
|
|
93
|
-
// `account.configured === false`. All other tools below require a valid
|
|
94
|
-
// token and are gated on `account.configured`.
|
|
95
|
-
// -----------------------------------------------------------------------
|
|
96
98
|
api.registerTool(
|
|
97
99
|
{
|
|
98
100
|
name: "clawchat_activate",
|
|
99
101
|
label: "Clawling: Activate (Login with Invite Code)",
|
|
100
102
|
description:
|
|
101
|
-
"Activate this
|
|
103
|
+
"Activate this OpenClaw plugin on ClawChat by exchanging an invite code for a token. " +
|
|
104
|
+
"Invite codes use six uppercase letters/digits, e.g. A1B2C3. " +
|
|
102
105
|
"TRIGGER — invoke this tool whenever the user's message matches ANY of: " +
|
|
103
|
-
"(1)
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
106
|
+
"(1) activation intent with an embedded invite code, such as 'activate ClawChat with invite code A1B2C3', " +
|
|
107
|
+
"'login to ClawChat with invite code A1B2C3', 'connect ClawChat using invite code A1B2C3', " +
|
|
108
|
+
"or '绑定 ClawChat,邀请码 A1B2C3' — call this tool with `code = \"A1B2C3\"`; " +
|
|
109
|
+
"(2) generic activation intent without an embedded code, such as 'activate ClawChat' or " +
|
|
110
|
+
"'login to ClawChat' — ask for invite code before calling the tool; " +
|
|
111
|
+
"(3) activation intent with an embedded code, such as 'use invite code A1B2C3', " +
|
|
112
|
+
"or the user pasting an invite code in the context of ClawChat activation. " +
|
|
108
113
|
"Extract the code verbatim — do NOT normalize / lowercase / add prefixes. " +
|
|
109
114
|
"On success the tool persists the resulting token + userId to the config, so " +
|
|
110
|
-
"subsequent `clawchat_*` calls work without
|
|
115
|
+
"subsequent `clawchat_*` calls work without another plugin registration pass.",
|
|
111
116
|
parameters: ClawchatActivateSchema,
|
|
112
117
|
async execute(_callId, params) {
|
|
113
|
-
const
|
|
114
|
-
const code = p.code?.trim();
|
|
118
|
+
const code = resolveActivateCode(params as ClawchatActivateParams & { command?: unknown });
|
|
115
119
|
if (!code) {
|
|
116
120
|
return validationError("openclaw-clawchat: code is required");
|
|
117
121
|
}
|
|
@@ -122,10 +126,13 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
122
126
|
accountId: null,
|
|
123
127
|
runtime: { log: (message: string) => api.logger.info?.(message) },
|
|
124
128
|
readInviteCode: async () => code,
|
|
129
|
+
mutateConfigFile: (api.runtime.config as unknown as {
|
|
130
|
+
mutateConfigFile: OpenclawClawchatMutateConfigFile;
|
|
131
|
+
}).mutateConfigFile,
|
|
125
132
|
});
|
|
126
133
|
return jsonResponse({
|
|
127
134
|
ok: true,
|
|
128
|
-
message: "
|
|
135
|
+
message: "ClawChat activated successfully.",
|
|
129
136
|
});
|
|
130
137
|
} catch (err) {
|
|
131
138
|
if (err instanceof ClawlingApiError) return apiError(err);
|
|
@@ -136,23 +143,14 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
136
143
|
{ name: "clawchat_activate" },
|
|
137
144
|
);
|
|
138
145
|
|
|
139
|
-
const account = resolveOpenclawClawlingAccount(api.config);
|
|
140
|
-
if (!account.configured) {
|
|
141
|
-
api.logger.debug?.(
|
|
142
|
-
"openclaw-clawchat: account not yet configured; only clawchat_activate is registered. " +
|
|
143
|
-
"The remaining tools will register on the next config reload after activation.",
|
|
144
|
-
);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
146
|
// Re-resolve at call time so config reloads pick up new tokens / baseUrl.
|
|
149
147
|
function resolveCurrent() {
|
|
150
148
|
return resolveOpenclawClawlingAccount(api.config!);
|
|
151
149
|
}
|
|
152
150
|
|
|
153
151
|
type ClientResult =
|
|
154
|
-
| { error:
|
|
155
|
-
| { client: ReturnType<typeof createOpenclawClawlingApiClient
|
|
152
|
+
| { ok: false; error: OpenClawAgentToolResult<unknown> }
|
|
153
|
+
| { ok: true; client: ReturnType<typeof createOpenclawClawlingApiClient> };
|
|
156
154
|
|
|
157
155
|
function buildClient(): ClientResult {
|
|
158
156
|
const acct = resolveCurrent();
|
|
@@ -160,9 +158,10 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
160
158
|
// only need to gate on `token` here (which is populated by `openclaw
|
|
161
159
|
// channel login`).
|
|
162
160
|
if (!acct.token) {
|
|
163
|
-
return { error: configError("openclaw-clawchat: token is required") };
|
|
161
|
+
return { ok: false, error: configError("openclaw-clawchat: token is required") };
|
|
164
162
|
}
|
|
165
163
|
return {
|
|
164
|
+
ok: true,
|
|
166
165
|
client: createOpenclawClawlingApiClient({
|
|
167
166
|
baseUrl: acct.baseUrl,
|
|
168
167
|
token: acct.token,
|
|
@@ -173,10 +172,10 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
173
172
|
|
|
174
173
|
function withClient<T>(
|
|
175
174
|
fn: (client: ReturnType<typeof createOpenclawClawlingApiClient>) => Promise<T>,
|
|
176
|
-
): Promise<
|
|
177
|
-
return (async (): Promise<
|
|
175
|
+
): Promise<OpenClawAgentToolResult<unknown>> {
|
|
176
|
+
return (async (): Promise<OpenClawAgentToolResult<unknown>> => {
|
|
178
177
|
const built = buildClient();
|
|
179
|
-
if (built.
|
|
178
|
+
if (!built.ok) return built.error;
|
|
180
179
|
try {
|
|
181
180
|
const data = await fn(built.client);
|
|
182
181
|
return jsonResponse(data);
|
|
@@ -189,39 +188,50 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
189
188
|
|
|
190
189
|
api.registerTool(
|
|
191
190
|
{
|
|
192
|
-
name: "
|
|
193
|
-
label: "Get Profile",
|
|
194
|
-
description:
|
|
195
|
-
|
|
191
|
+
name: "clawchat_get_account_profile",
|
|
192
|
+
label: "Get ClawChat Account Profile",
|
|
193
|
+
description:
|
|
194
|
+
"Fetch the configured ClawChat account profile (user id, nickname/display name, avatar, bio). " +
|
|
195
|
+
"TRIGGER — invoke when the user asks for the ClawChat account/profile connected to this plugin, " +
|
|
196
|
+
"such as 'show my ClawChat profile', 'what is the configured ClawChat account?', " +
|
|
197
|
+
"'当前 ClawChat 账号资料', or 'ClawChat 昵称头像简介'. " +
|
|
198
|
+
"Do not use this for OpenClaw agent persona/profile questions unless the user explicitly means the ClawChat account.",
|
|
199
|
+
parameters: ClawchatGetAccountProfileSchema,
|
|
196
200
|
async execute(_callId, _params) {
|
|
197
201
|
return await withClient((c) => c.getMyProfile());
|
|
198
202
|
},
|
|
199
203
|
},
|
|
200
|
-
{ name: "
|
|
204
|
+
{ name: "clawchat_get_account_profile" },
|
|
201
205
|
);
|
|
202
206
|
|
|
203
207
|
api.registerTool(
|
|
204
208
|
{
|
|
205
|
-
name: "
|
|
206
|
-
label: "Get User
|
|
207
|
-
description:
|
|
208
|
-
|
|
209
|
+
name: "clawchat_get_user_profile",
|
|
210
|
+
label: "Get ClawChat User Profile",
|
|
211
|
+
description:
|
|
212
|
+
"Fetch a ClawChat user's public profile by userId. " +
|
|
213
|
+
"TRIGGER — invoke when the user asks to look up, view, or inspect a specific ClawChat user's public profile " +
|
|
214
|
+
"and provides a concrete userId. Do not guess or infer userId from a nickname/display name.",
|
|
215
|
+
parameters: ClawchatGetUserProfileSchema,
|
|
209
216
|
async execute(_callId, params) {
|
|
210
|
-
const p = params as
|
|
217
|
+
const p = params as ClawchatGetUserProfileParams;
|
|
211
218
|
return await withClient((c) => c.getUserInfo(p.userId));
|
|
212
219
|
},
|
|
213
220
|
},
|
|
214
|
-
{ name: "
|
|
221
|
+
{ name: "clawchat_get_user_profile" },
|
|
215
222
|
);
|
|
216
223
|
|
|
217
224
|
api.registerTool(
|
|
218
225
|
{
|
|
219
|
-
name: "
|
|
220
|
-
label: "List Friends",
|
|
221
|
-
description:
|
|
222
|
-
|
|
226
|
+
name: "clawchat_list_account_friends",
|
|
227
|
+
label: "List ClawChat Account Friends",
|
|
228
|
+
description:
|
|
229
|
+
"List the configured ClawChat account's friends/contacts, paginated (page=1, pageSize=20 by default). " +
|
|
230
|
+
"TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, friend list, " +
|
|
231
|
+
"or asks to show more friends with pagination.",
|
|
232
|
+
parameters: ClawchatListAccountFriendsSchema,
|
|
223
233
|
async execute(_callId, params) {
|
|
224
|
-
const p = (params ?? {}) as
|
|
234
|
+
const p = (params ?? {}) as ClawchatListAccountFriendsParams;
|
|
225
235
|
return await withClient((c) =>
|
|
226
236
|
c.listFriends({
|
|
227
237
|
...(p.page !== undefined ? { page: p.page } : { page: 1 }),
|
|
@@ -230,32 +240,31 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
230
240
|
);
|
|
231
241
|
},
|
|
232
242
|
},
|
|
233
|
-
{ name: "
|
|
243
|
+
{ name: "clawchat_list_account_friends" },
|
|
234
244
|
);
|
|
235
245
|
|
|
236
246
|
api.registerTool(
|
|
237
247
|
{
|
|
238
|
-
name: "
|
|
239
|
-
label: "Update Profile",
|
|
248
|
+
name: "clawchat_update_account_profile",
|
|
249
|
+
label: "Update ClawChat Account Profile",
|
|
240
250
|
description:
|
|
241
|
-
"Update
|
|
242
|
-
"TRIGGER — invoke this tool whenever the user's message
|
|
243
|
-
"(1) nickname/name change: 'change
|
|
244
|
-
"'
|
|
245
|
-
"
|
|
246
|
-
"(2) avatar change
|
|
247
|
-
"'
|
|
248
|
-
"
|
|
249
|
-
"
|
|
250
|
-
"
|
|
251
|
-
"
|
|
252
|
-
"'change your self introduction', '把简介改成 X', '更新自我介绍', '个人简介改为 X' " +
|
|
251
|
+
"Update the configured ClawChat account profile (nickname and/or avatar and/or bio). " +
|
|
252
|
+
"TRIGGER — invoke this tool whenever the user's message explicitly asks to change the ClawChat account profile: " +
|
|
253
|
+
"(1) ClawChat account nickname/name change: 'change the ClawChat account nickname to X', " +
|
|
254
|
+
"'set this ClawChat account name to X', 'ClawChat 昵称改为 X', '账号昵称改成 X', '账号名字叫 X' " +
|
|
255
|
+
"→ call with `nickname = X`; " +
|
|
256
|
+
"(2) ClawChat account avatar/profile-picture change: 'change the ClawChat account avatar', " +
|
|
257
|
+
"'use this image as the ClawChat profile picture', 'ClawChat 头像改为 …', '账号头像换成 …' " +
|
|
258
|
+
"→ first obtain the avatar URL (upload via `clawchat_upload_avatar_image`, OR use a provided URL directly), " +
|
|
259
|
+
"then call this tool with `avatar_url = <url>`; " +
|
|
260
|
+
"(3) ClawChat account bio/self-introduction change: 'update the ClawChat bio', " +
|
|
261
|
+
"'set the ClawChat account self-introduction to X', 'ClawChat 简介改成 X', '账号简介改为 X', '个人简介改为 X' " +
|
|
253
262
|
"→ call with `bio = X`. " +
|
|
254
263
|
"You can pass `nickname`, `avatar_url`, and `bio` together in one call, or just one of them. " +
|
|
255
|
-
"At least one of the three must be present.",
|
|
256
|
-
parameters:
|
|
264
|
+
"At least one of the three must be present. Do not use this for OpenClaw agent persona changes unless the user explicitly refers to the ClawChat account.",
|
|
265
|
+
parameters: ClawchatUpdateAccountProfileSchema,
|
|
257
266
|
async execute(_callId, params) {
|
|
258
|
-
const p = (params ?? {}) as
|
|
267
|
+
const p = (params ?? {}) as ClawchatUpdateAccountProfileParams;
|
|
259
268
|
const patch: { nickname?: string; avatar_url?: string; bio?: string } = {};
|
|
260
269
|
if (typeof p.nickname === "string") patch.nickname = p.nickname;
|
|
261
270
|
if (typeof p.avatar_url === "string") patch.avatar_url = p.avatar_url;
|
|
@@ -268,19 +277,20 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
268
277
|
return await withClient((c): Promise<Profile> => c.updateMyProfile(patch));
|
|
269
278
|
},
|
|
270
279
|
},
|
|
271
|
-
{ name: "
|
|
280
|
+
{ name: "clawchat_update_account_profile" },
|
|
272
281
|
);
|
|
273
282
|
|
|
274
283
|
api.registerTool(
|
|
275
284
|
{
|
|
276
|
-
name: "
|
|
277
|
-
label: "Upload Avatar",
|
|
285
|
+
name: "clawchat_upload_avatar_image",
|
|
286
|
+
label: "Upload ClawChat Avatar Image",
|
|
278
287
|
description:
|
|
279
|
-
"Upload a local
|
|
280
|
-
"
|
|
281
|
-
|
|
288
|
+
"Upload a local image file to ClawChat avatar storage (max 20MB) and return the hosted avatar URL. " +
|
|
289
|
+
"TRIGGER — invoke when the user provides an absolute local image path and asks to upload it for the ClawChat account avatar/profile picture. " +
|
|
290
|
+
"This tool does not update or set the account avatar by itself; call `clawchat_update_account_profile` with `avatar_url` after this tool returns a URL.",
|
|
291
|
+
parameters: ClawchatUploadAvatarImageSchema,
|
|
282
292
|
async execute(_callId, params) {
|
|
283
|
-
const p = params as
|
|
293
|
+
const p = params as ClawchatUploadAvatarImageParams;
|
|
284
294
|
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
285
295
|
return validationError("openclaw-clawchat: filePath must be an absolute local path");
|
|
286
296
|
}
|
|
@@ -306,18 +316,20 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
306
316
|
return await withClient((c) => c.uploadAvatar({ buffer, filename, mime }));
|
|
307
317
|
},
|
|
308
318
|
},
|
|
309
|
-
{ name: "
|
|
319
|
+
{ name: "clawchat_upload_avatar_image" },
|
|
310
320
|
);
|
|
311
321
|
|
|
312
322
|
api.registerTool(
|
|
313
323
|
{
|
|
314
|
-
name: "
|
|
315
|
-
label: "Upload File",
|
|
324
|
+
name: "clawchat_upload_media_file",
|
|
325
|
+
label: "Upload ClawChat Media File",
|
|
316
326
|
description:
|
|
317
|
-
"Upload a local file to
|
|
318
|
-
|
|
327
|
+
"Upload a local file or media file to ClawChat media storage (max 20MB) and return the public URL/shareable URL. " +
|
|
328
|
+
"TRIGGER — invoke when the user provides an absolute local file path and asks to upload, share, or create a ClawChat-accessible link for that file. " +
|
|
329
|
+
"Do not use this for account avatar changes; use `clawchat_upload_avatar_image` for avatar images.",
|
|
330
|
+
parameters: ClawchatUploadMediaFileSchema,
|
|
319
331
|
async execute(_callId, params) {
|
|
320
|
-
const p = params as
|
|
332
|
+
const p = params as ClawchatUploadMediaFileParams;
|
|
321
333
|
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
322
334
|
return validationError("openclaw-clawchat: filePath must be an absolute local path");
|
|
323
335
|
}
|
|
@@ -343,10 +355,10 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
343
355
|
return await withClient((c) => c.uploadMedia({ buffer, filename, mime }));
|
|
344
356
|
},
|
|
345
357
|
},
|
|
346
|
-
{ name: "
|
|
358
|
+
{ name: "clawchat_upload_media_file" },
|
|
347
359
|
);
|
|
348
360
|
|
|
349
|
-
api.logger.
|
|
350
|
-
"openclaw-clawchat: registered 7 clawchat_* tools (activate,
|
|
361
|
+
api.logger.debug?.(
|
|
362
|
+
"openclaw-clawchat: registered 7 clawchat_* tools (activate, get_account_profile, get_user_profile, list_account_friends, update_account_profile, upload_avatar_image, upload_media_file)",
|
|
351
363
|
);
|
|
352
364
|
}
|