clawsocial-plugin 1.5.0 → 1.6.0
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 +21 -3
- package/README.zh.md +21 -3
- package/index.ts +35 -36
- package/openclaw.plugin.json +13 -1
- package/package.json +1 -1
- package/src/api.ts +1 -1
- package/src/i18n.ts +142 -0
- package/src/local-server.ts +59 -27
- package/src/store.ts +8 -2
- package/src/tools/block.ts +5 -4
- package/src/tools/connect.ts +7 -7
- package/src/tools/find.ts +16 -15
- package/src/tools/inbox.ts +16 -15
- package/src/tools/match.ts +6 -5
- package/src/tools/notify_settings.ts +9 -9
- package/src/tools/open_inbox.ts +2 -1
- package/src/tools/open_local_inbox.ts +2 -1
- package/src/tools/register.ts +19 -4
- package/src/tools/session_get.ts +8 -7
- package/src/tools/session_send.ts +6 -5
- package/src/tools/sessions_list.ts +7 -6
- package/src/tools/update_profile.ts +4 -3
- package/src/ws-client.ts +18 -25
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { AnyAgentTool } from "../types.js";
|
|
3
3
|
import { startLocalServer } from "../local-server.js";
|
|
4
|
+
import { t } from "../i18n.js";
|
|
4
5
|
|
|
5
6
|
export function createOpenLocalInboxTool(): AnyAgentTool {
|
|
6
7
|
return {
|
|
@@ -16,7 +17,7 @@ export function createOpenLocalInboxTool(): AnyAgentTool {
|
|
|
16
17
|
type: "text",
|
|
17
18
|
text: JSON.stringify({
|
|
18
19
|
url,
|
|
19
|
-
message:
|
|
20
|
+
message: t("tools_local_inbox", { url }),
|
|
20
21
|
}),
|
|
21
22
|
}],
|
|
22
23
|
};
|
package/src/tools/register.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Type } from "@sinclair/typebox";
|
|
|
2
2
|
import type { AnyAgentTool } from "../types.js";
|
|
3
3
|
import api from "../api.js";
|
|
4
4
|
import { getState, setState } from "../store.js";
|
|
5
|
+
import { reconnectWsClient } from "../ws-client.js";
|
|
6
|
+
import { t } from "../i18n.js";
|
|
5
7
|
|
|
6
8
|
export function createRegisterTool(): AnyAgentTool {
|
|
7
9
|
return {
|
|
@@ -10,12 +12,19 @@ export function createRegisterTool(): AnyAgentTool {
|
|
|
10
12
|
description:
|
|
11
13
|
"Register this lobster on ClawSocial. Call ONCE automatically on first use. Only asks the user for a public_name.",
|
|
12
14
|
parameters: Type.Object({
|
|
13
|
-
public_name: Type.String({ description: "
|
|
15
|
+
public_name: Type.String({ description: "Public name for this lobster" }),
|
|
16
|
+
language_pref: Type.Optional(
|
|
17
|
+
Type.Unsafe<"zh" | "en">({
|
|
18
|
+
type: "string",
|
|
19
|
+
enum: ["zh", "en"],
|
|
20
|
+
description: "Language preference: zh (Chinese) or en (English). Default: en",
|
|
21
|
+
}),
|
|
22
|
+
),
|
|
14
23
|
availability: Type.Optional(
|
|
15
24
|
Type.Unsafe<"open" | "by-request" | "closed">({
|
|
16
25
|
type: "string",
|
|
17
26
|
enum: ["open", "by-request", "closed"],
|
|
18
|
-
description: "
|
|
27
|
+
description: "Discoverability, default open",
|
|
19
28
|
}),
|
|
20
29
|
),
|
|
21
30
|
}),
|
|
@@ -30,9 +39,11 @@ export function createRegisterTool(): AnyAgentTool {
|
|
|
30
39
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
31
40
|
}
|
|
32
41
|
|
|
42
|
+
const langPref = (params.language_pref as string) ?? "en";
|
|
33
43
|
const res = await api.register({
|
|
34
44
|
public_name: params.public_name as string,
|
|
35
45
|
availability: (params.availability as string) ?? "open",
|
|
46
|
+
language_pref: langPref,
|
|
36
47
|
});
|
|
37
48
|
|
|
38
49
|
setState({
|
|
@@ -40,13 +51,17 @@ export function createRegisterTool(): AnyAgentTool {
|
|
|
40
51
|
api_key: res.api_key,
|
|
41
52
|
token: res.token,
|
|
42
53
|
public_name: res.public_name,
|
|
43
|
-
registered_at: Date.now(),
|
|
54
|
+
registered_at: Math.floor(Date.now() / 1000),
|
|
55
|
+
lang: langPref,
|
|
44
56
|
});
|
|
45
57
|
|
|
58
|
+
// Start WebSocket connection now that credentials are available
|
|
59
|
+
reconnectWsClient();
|
|
60
|
+
|
|
46
61
|
const result = {
|
|
47
62
|
agent_id: res.agent_id,
|
|
48
63
|
public_name: res.public_name,
|
|
49
|
-
message:
|
|
64
|
+
message: t("tools_registered", { name: res.public_name }),
|
|
50
65
|
};
|
|
51
66
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
52
67
|
},
|
package/src/tools/session_get.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { AnyAgentTool } from "../types.js";
|
|
3
3
|
import { getSessions, markRead } from "../store.js";
|
|
4
|
+
import { t, formatDateTime } from "../i18n.js";
|
|
4
5
|
|
|
5
6
|
export function createSessionGetTool(serverUrl: string): AnyAgentTool {
|
|
6
7
|
return {
|
|
@@ -9,9 +10,9 @@ export function createSessionGetTool(serverUrl: string): AnyAgentTool {
|
|
|
9
10
|
description:
|
|
10
11
|
"Get recent messages of a specific session. Supports exact session_id or fuzzy partner_name match.",
|
|
11
12
|
parameters: Type.Object({
|
|
12
|
-
session_id: Type.Optional(Type.String({ description: "
|
|
13
|
+
session_id: Type.Optional(Type.String({ description: "Exact UUID (provide either this or partner_name)" })),
|
|
13
14
|
partner_name: Type.Optional(
|
|
14
|
-
Type.String({ description: "
|
|
15
|
+
Type.String({ description: "Fuzzy match by partner name (provide either this or session_id)" }),
|
|
15
16
|
),
|
|
16
17
|
}),
|
|
17
18
|
async execute(_id: string, params: Record<string, unknown>) {
|
|
@@ -33,7 +34,7 @@ export function createSessionGetTool(serverUrl: string): AnyAgentTool {
|
|
|
33
34
|
if (!session) {
|
|
34
35
|
const result = {
|
|
35
36
|
found: false,
|
|
36
|
-
message: "
|
|
37
|
+
message: t("tools_session_404"),
|
|
37
38
|
};
|
|
38
39
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
39
40
|
}
|
|
@@ -43,7 +44,7 @@ export function createSessionGetTool(serverUrl: string): AnyAgentTool {
|
|
|
43
44
|
const shortId = session.partner_agent_id ? "#" + session.partner_agent_id.slice(0, 6) : "";
|
|
44
45
|
const partnerDisplay = session.partner_name
|
|
45
46
|
? `${session.partner_name} ${shortId}`
|
|
46
|
-
: (session.partner_agent_id ?? "
|
|
47
|
+
: (session.partner_agent_id ?? t("unknown"));
|
|
47
48
|
const messages = (session.messages ?? []).slice(-10);
|
|
48
49
|
const sessionUrl = `${serverUrl}/inbox/session/${session.id}`;
|
|
49
50
|
|
|
@@ -52,12 +53,12 @@ export function createSessionGetTool(serverUrl: string): AnyAgentTool {
|
|
|
52
53
|
partner_name: partnerDisplay,
|
|
53
54
|
status: session.status,
|
|
54
55
|
recent_messages: messages.map((m) => ({
|
|
55
|
-
from: m.from_self ? "
|
|
56
|
+
from: m.from_self ? t("tools_my_lobster") : partnerDisplay,
|
|
56
57
|
content: m.content,
|
|
57
|
-
time: m.created_at ?
|
|
58
|
+
time: m.created_at ? formatDateTime(m.created_at) : "",
|
|
58
59
|
})),
|
|
59
60
|
session_url: sessionUrl,
|
|
60
|
-
tip:
|
|
61
|
+
tip: `View in browser: ${sessionUrl} (login via clawsocial_open_inbox first)`,
|
|
61
62
|
};
|
|
62
63
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
63
64
|
},
|
|
@@ -2,6 +2,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
2
2
|
import type { AnyAgentTool } from "../types.js";
|
|
3
3
|
import api from "../api.js";
|
|
4
4
|
import { addMessage } from "../store.js";
|
|
5
|
+
import { t } from "../i18n.js";
|
|
5
6
|
|
|
6
7
|
export function createSessionSendTool(): AnyAgentTool {
|
|
7
8
|
return {
|
|
@@ -10,14 +11,14 @@ export function createSessionSendTool(): AnyAgentTool {
|
|
|
10
11
|
description:
|
|
11
12
|
"Send a message in an active session on behalf of the user. Call when the user explicitly provides reply content. Pass the content verbatim — do not paraphrase.",
|
|
12
13
|
parameters: Type.Object({
|
|
13
|
-
session_id: Type.String({ description: "
|
|
14
|
-
content: Type.String({ description: "
|
|
14
|
+
session_id: Type.String({ description: "Active session ID" }),
|
|
15
|
+
content: Type.String({ description: "User's message, forwarded verbatim" }),
|
|
15
16
|
}),
|
|
16
17
|
async execute(_id: string, params: Record<string, unknown>) {
|
|
17
18
|
const session_id = params.session_id as string;
|
|
18
19
|
const content = params.content as string;
|
|
19
|
-
if (!session_id) throw new Error("session_id
|
|
20
|
-
if (!content) throw new Error("content
|
|
20
|
+
if (!session_id) throw new Error("session_id is required");
|
|
21
|
+
if (!content) throw new Error("content is required");
|
|
21
22
|
|
|
22
23
|
const res = await api.sendMessage(session_id, { content, intent: "chat" });
|
|
23
24
|
|
|
@@ -32,7 +33,7 @@ export function createSessionSendTool(): AnyAgentTool {
|
|
|
32
33
|
const result = {
|
|
33
34
|
msg_id: res.msg_id,
|
|
34
35
|
delivered: res.delivered,
|
|
35
|
-
message: res.delivered ? "
|
|
36
|
+
message: res.delivered ? t("tools_msg_delivered") : t("tools_msg_queued"),
|
|
36
37
|
};
|
|
37
38
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
38
39
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { AnyAgentTool } from "../types.js";
|
|
3
3
|
import { getSessions } from "../store.js";
|
|
4
|
+
import { t, formatDateTime } from "../i18n.js";
|
|
4
5
|
|
|
5
6
|
export function createSessionsListTool(serverUrl: string): AnyAgentTool {
|
|
6
7
|
return {
|
|
@@ -16,7 +17,7 @@ export function createSessionsListTool(serverUrl: string): AnyAgentTool {
|
|
|
16
17
|
if (list.length === 0) {
|
|
17
18
|
const result = {
|
|
18
19
|
sessions: [],
|
|
19
|
-
message: "
|
|
20
|
+
message: "No sessions yet. Use clawsocial_match to discover people by interest, or clawsocial_find to locate someone by name, then clawsocial_connect to start a conversation.",
|
|
20
21
|
};
|
|
21
22
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
22
23
|
}
|
|
@@ -26,22 +27,22 @@ export function createSessionsListTool(serverUrl: string): AnyAgentTool {
|
|
|
26
27
|
session_id: s.id,
|
|
27
28
|
partner_name: s.partner_name
|
|
28
29
|
? `${s.partner_name} ${shortId(s.partner_agent_id)}`
|
|
29
|
-
: (s.partner_agent_id ?? "
|
|
30
|
+
: (s.partner_agent_id ?? t("unknown")),
|
|
30
31
|
status: s.status,
|
|
31
32
|
last_message: s.last_message
|
|
32
33
|
? s.last_message.slice(0, 60) + (s.last_message.length > 60 ? "..." : "")
|
|
33
|
-
: "
|
|
34
|
+
: t("inbox_no_preview"),
|
|
34
35
|
unread: s.unread ?? 0,
|
|
35
36
|
last_active: s.last_active_at
|
|
36
|
-
?
|
|
37
|
-
: "
|
|
37
|
+
? formatDateTime(s.last_active_at)
|
|
38
|
+
: t("unknown"),
|
|
38
39
|
}));
|
|
39
40
|
|
|
40
41
|
const result = {
|
|
41
42
|
sessions: formatted,
|
|
42
43
|
total: list.length,
|
|
43
44
|
unread_total: list.reduce((sum, s) => sum + (s.unread ?? 0), 0),
|
|
44
|
-
tip:
|
|
45
|
+
tip: `Use clawsocial_open_inbox to get an inbox login link (${serverUrl}/inbox)`,
|
|
45
46
|
};
|
|
46
47
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
47
48
|
},
|
|
@@ -2,6 +2,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
2
2
|
import type { AnyAgentTool } from "../types.js";
|
|
3
3
|
import api from "../api.js";
|
|
4
4
|
import { getState } from "../store.js";
|
|
5
|
+
import { t } from "../i18n.js";
|
|
5
6
|
|
|
6
7
|
export function createUpdateProfileTool(): AnyAgentTool {
|
|
7
8
|
return {
|
|
@@ -50,7 +51,7 @@ export function createUpdateProfileTool(): AnyAgentTool {
|
|
|
50
51
|
{
|
|
51
52
|
type: "text",
|
|
52
53
|
text: JSON.stringify({
|
|
53
|
-
error: "
|
|
54
|
+
error: t("tools_not_registered"),
|
|
54
55
|
}),
|
|
55
56
|
},
|
|
56
57
|
],
|
|
@@ -69,7 +70,7 @@ export function createUpdateProfileTool(): AnyAgentTool {
|
|
|
69
70
|
content: [
|
|
70
71
|
{
|
|
71
72
|
type: "text",
|
|
72
|
-
text: JSON.stringify({ error: "
|
|
73
|
+
text: JSON.stringify({ error: t("tools_no_update") }),
|
|
73
74
|
},
|
|
74
75
|
],
|
|
75
76
|
};
|
|
@@ -83,7 +84,7 @@ export function createUpdateProfileTool(): AnyAgentTool {
|
|
|
83
84
|
type: "text",
|
|
84
85
|
text: JSON.stringify({
|
|
85
86
|
ok: true,
|
|
86
|
-
message: "
|
|
87
|
+
message: t("tools_profile_updated"),
|
|
87
88
|
updated: Object.keys(body),
|
|
88
89
|
}),
|
|
89
90
|
},
|
package/src/ws-client.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
2
|
import { getState, upsertSession, getSession, addMessage, markRead, getSettings } from "./store.js";
|
|
3
3
|
import { pushNotification } from "./notify.js";
|
|
4
|
+
import { t } from "./i18n.js";
|
|
4
5
|
|
|
5
6
|
let ws: WebSocket | null = null;
|
|
6
7
|
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -22,7 +23,7 @@ function maybePush(detailText: string): void {
|
|
|
22
23
|
const mode = getSettings().notifyMode;
|
|
23
24
|
if (mode === "silent") return;
|
|
24
25
|
if (mode === "minimal") {
|
|
25
|
-
pushNotification("
|
|
26
|
+
pushNotification(t("ws_new_msg_notify"));
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
pushNotification(detailText);
|
|
@@ -31,11 +32,11 @@ function maybePush(detailText: string): void {
|
|
|
31
32
|
function handleServerMessage(msg: Record<string, unknown>): void {
|
|
32
33
|
switch (msg.type) {
|
|
33
34
|
case "auth_ok":
|
|
34
|
-
log(
|
|
35
|
+
log(`${t("ws_auth_ok")}: ${msg.agent_id}`);
|
|
35
36
|
break;
|
|
36
37
|
|
|
37
38
|
case "auth_error":
|
|
38
|
-
console.error(`[ClawSocial WS]
|
|
39
|
+
console.error(`[ClawSocial WS] ${t("ws_auth_fail")}: ${msg.error}`);
|
|
39
40
|
break;
|
|
40
41
|
|
|
41
42
|
case "ping":
|
|
@@ -44,36 +45,32 @@ function handleServerMessage(msg: Record<string, unknown>): void {
|
|
|
44
45
|
|
|
45
46
|
case "connect_request": {
|
|
46
47
|
const sid = msg.session_id as string;
|
|
48
|
+
const name = msg.from_agent_name as string;
|
|
47
49
|
upsertSession(sid, {
|
|
48
50
|
status: "pending",
|
|
49
51
|
is_receiver: true,
|
|
50
52
|
partner_agent_id: msg.from_agent_id as string,
|
|
51
|
-
partner_name:
|
|
53
|
+
partner_name: name,
|
|
52
54
|
intro_message: (msg.intro_message as string) || "",
|
|
53
55
|
messages: [],
|
|
54
56
|
unread: 0,
|
|
55
57
|
created_at: Math.floor(Date.now() / 1000),
|
|
56
58
|
});
|
|
57
|
-
log(
|
|
58
|
-
|
|
59
|
-
);
|
|
60
|
-
maybePush(
|
|
61
|
-
`[ClawSocial] 收到来自 ${msg.from_agent_name} 的连接请求。可调用 clawsocial_open_inbox 查看。`,
|
|
62
|
-
);
|
|
59
|
+
log(t("ws_connect_req", { name: `${name}${shortId(msg.from_agent_id as string)}` }));
|
|
60
|
+
maybePush(t("ws_connect_req_notify", { name }));
|
|
63
61
|
break;
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
case "session_started": {
|
|
67
65
|
const sid = msg.session_id as string;
|
|
66
|
+
const name = msg.with_agent_name as string;
|
|
68
67
|
upsertSession(sid, {
|
|
69
68
|
status: "active",
|
|
70
69
|
partner_agent_id: msg.with_agent_id as string,
|
|
71
|
-
partner_name:
|
|
70
|
+
partner_name: name,
|
|
72
71
|
});
|
|
73
|
-
log(`${
|
|
74
|
-
maybePush(
|
|
75
|
-
`[ClawSocial] ${msg.with_agent_name} 开始了与你的会话。可调用 clawsocial_session_get 查看消息。`,
|
|
76
|
-
);
|
|
72
|
+
log(t("ws_session_accepted", { name: `${name}${shortId(msg.with_agent_id as string)}`, id: sid }));
|
|
73
|
+
maybePush(t("ws_session_notify", { name }));
|
|
77
74
|
break;
|
|
78
75
|
}
|
|
79
76
|
|
|
@@ -102,12 +99,8 @@ function handleServerMessage(msg: Record<string, unknown>): void {
|
|
|
102
99
|
intent: msg.intent as string | undefined,
|
|
103
100
|
created_at: (msg.created_at as number) || Math.floor(Date.now() / 1000),
|
|
104
101
|
});
|
|
105
|
-
log(
|
|
106
|
-
|
|
107
|
-
);
|
|
108
|
-
maybePush(
|
|
109
|
-
`[ClawSocial] 收到 ${partnerName} 的新消息:${(msg.content as string).slice(0, 80)}`,
|
|
110
|
-
);
|
|
102
|
+
log(t("ws_msg_log", { name: `${partnerName}${shortId(msg.from_agent as string)}`, preview: (msg.content as string).slice(0, 60) }));
|
|
103
|
+
maybePush(t("ws_msg_notify", { name: partnerName, preview: (msg.content as string).slice(0, 80) }));
|
|
111
104
|
break;
|
|
112
105
|
}
|
|
113
106
|
|
|
@@ -130,7 +123,7 @@ export function startWsClient(serverUrl: string): void {
|
|
|
130
123
|
function connect(): void {
|
|
131
124
|
const state = getState();
|
|
132
125
|
if (!state.agent_id || !state.api_key) {
|
|
133
|
-
log("
|
|
126
|
+
log(t("ws_not_registered"));
|
|
134
127
|
return;
|
|
135
128
|
}
|
|
136
129
|
|
|
@@ -140,7 +133,7 @@ function connect(): void {
|
|
|
140
133
|
|
|
141
134
|
ws.on("open", () => {
|
|
142
135
|
const s = getState();
|
|
143
|
-
log("
|
|
136
|
+
log(t("ws_connected"));
|
|
144
137
|
ws!.send(JSON.stringify({ type: "auth", agent_id: s.agent_id, api_key: s.api_key }));
|
|
145
138
|
});
|
|
146
139
|
|
|
@@ -155,13 +148,13 @@ function connect(): void {
|
|
|
155
148
|
});
|
|
156
149
|
|
|
157
150
|
ws.on("close", (code: number) => {
|
|
158
|
-
log(
|
|
151
|
+
log(`${t("ws_disconnected")} (${code}), ${t("ws_reconnect")}`);
|
|
159
152
|
ws = null;
|
|
160
153
|
reconnectTimer = setTimeout(connect, 5000);
|
|
161
154
|
});
|
|
162
155
|
|
|
163
156
|
ws.on("error", (err: Error) => {
|
|
164
|
-
console.error("[ClawSocial WS]
|
|
157
|
+
console.error("[ClawSocial WS] Error:", err.message);
|
|
165
158
|
});
|
|
166
159
|
}
|
|
167
160
|
|