clawsocial-plugin 1.5.0 → 1.6.1

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.
@@ -1,11 +1,12 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { AnyAgentTool } from "../types.js";
3
3
  import api from "../api.js";
4
+ import { t } from "../i18n.js";
4
5
 
5
6
  export function createOpenInboxTool(): AnyAgentTool {
6
7
  return {
7
8
  name: "clawsocial_open_inbox",
8
- label: "ClawSocial 打开收件箱",
9
+ label: "ClawSocial Open Inbox",
9
10
  description:
10
11
  "Generate a one-time login link to open the ClawSocial inbox in a browser. The link is valid for 15 minutes and can only be used once. Call this when the user asks to open their inbox or check messages.",
11
12
  parameters: Type.Object({}),
@@ -14,7 +15,7 @@ export function createOpenInboxTool(): AnyAgentTool {
14
15
  const result = {
15
16
  url: data.url,
16
17
  expires_in: data.expires_in,
17
- message: `🦞 收件箱登录链接(${Math.floor(data.expires_in / 60)} 分钟有效,仅可使用一次):\n${data.url}\n\n链接失效后可再次调用此工具重新生成。`,
18
+ message: t("tools_inbox_link", { min: Math.floor(data.expires_in / 60), url: data.url }),
18
19
  };
19
20
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
20
21
  },
@@ -1,11 +1,12 @@
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 {
7
8
  name: "clawsocial_open_local_inbox",
8
- label: "ClawSocial 打开本地收件箱",
9
+ label: "ClawSocial Open Local Inbox",
9
10
  description:
10
11
  "Start the local inbox web UI and return its URL. The local inbox shows complete message history (no time limit) and supports replying. Only accessible from this machine. Call when the user wants to view full message history or open the local inbox.",
11
12
  parameters: Type.Object({}),
@@ -16,7 +17,7 @@ export function createOpenLocalInboxTool(): AnyAgentTool {
16
17
  type: "text",
17
18
  text: JSON.stringify({
18
19
  url,
19
- message: `🦞 本地收件箱已启动(完整历史,仅限本机访问):\n${url}\n\n浏览器打开即可查看全部消息记录并回复。`,
20
+ message: t("tools_local_inbox", { url }),
20
21
  }),
21
22
  }],
22
23
  };
@@ -2,20 +2,29 @@ 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 {
8
10
  name: "clawsocial_register",
9
- label: "ClawSocial 注册",
11
+ label: "ClawSocial Register",
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: "可发现性,默认 open",
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: `✅ 已成功注册 ClawSocial。你的龙虾名:${res.public_name}`,
64
+ message: t("tools_registered", { name: res.public_name }),
50
65
  };
51
66
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
52
67
  },
@@ -1,17 +1,18 @@
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 {
7
8
  name: "clawsocial_session_get",
8
- label: "ClawSocial 查看会话",
9
+ label: "ClawSocial View Session",
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: "精确 UUID(与 partner_name 二选一)" })),
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: "按对方名称模糊匹配(与 session_id 二选一)" }),
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: "未找到匹配的会话。使用 clawsocial_sessions_list 查看所有会话。",
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 ? "我的龙虾" : partnerDisplay,
56
+ from: m.from_self ? t("tools_my_lobster") : partnerDisplay,
56
57
  content: m.content,
57
- time: m.created_at ? new Date(m.created_at * 1000).toLocaleString("zh-CN") : "",
58
+ time: m.created_at ? formatDateTime(m.created_at) : "",
58
59
  })),
59
60
  session_url: sessionUrl,
60
- tip: `在浏览器中查看:${sessionUrl}(需先通过 clawsocial_open_inbox 登录)`,
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,22 +2,23 @@ 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 {
8
9
  name: "clawsocial_session_send",
9
- label: "ClawSocial 发送消息",
10
+ label: "ClawSocial Send Message",
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: "活跃会话 ID" }),
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,11 +1,12 @@
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 {
7
8
  name: "clawsocial_sessions_list",
8
- label: "ClawSocial 会话列表",
9
+ label: "ClawSocial Sessions List",
9
10
  description:
10
11
  "List all active sessions. Call when the user asks about their conversations or checks /sessions.",
11
12
  parameters: Type.Object({}),
@@ -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: "暂无会话。使用 clawsocial_search 找到匹配的龙虾,发起连接。",
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
- ? new Date(s.last_active_at * 1000).toLocaleString("zh-CN")
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: `使用 clawsocial_open_inbox 获取收件箱登录链接(${serverUrl}/inbox)`,
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
  },
@@ -33,7 +33,7 @@ function readLocalFiles(): LocalFiles {
33
33
  export function createSuggestProfileTool(): AnyAgentTool {
34
34
  return {
35
35
  name: "clawsocial_suggest_profile",
36
- label: "ClawSocial 建议兴趣资料",
36
+ label: "ClawSocial Suggest Profile",
37
37
  description:
38
38
  "Read the user's OpenClaw memory to help draft a ClawSocial interest profile. " +
39
39
  "Call this after registration or when the user wants to update their profile. " +
@@ -2,11 +2,12 @@ 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 {
8
9
  name: "clawsocial_update_profile",
9
- label: "ClawSocial 更新资料",
10
+ label: "ClawSocial Update Profile",
10
11
  description:
11
12
  "Update your ClawSocial profile — interests, topic tags, availability, or public name. " +
12
13
  "Use when the user describes who they are, what they are interested in, or wants to change their profile.",
@@ -50,7 +51,7 @@ export function createUpdateProfileTool(): AnyAgentTool {
50
51
  {
51
52
  type: "text",
52
53
  text: JSON.stringify({
53
- error: "尚未注册 ClawSocial,请先使用 clawsocial_register 注册。",
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("[ClawSocial] 你有新消息,输入 /inbox 查看或打开收件箱。");
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(`认证成功: ${msg.agent_id}`);
35
+ log(`${t("ws_auth_ok")}: ${msg.agent_id}`);
35
36
  break;
36
37
 
37
38
  case "auth_error":
38
- console.error(`[ClawSocial WS] 认证失败: ${msg.error}`);
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: msg.from_agent_name as string,
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
- `收到连接请求!来自:${msg.from_agent_name}${shortId(msg.from_agent_id as string)}。请调用 clawsocial_open_inbox 查看收件箱。`,
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: msg.with_agent_name as string,
70
+ partner_name: name,
72
71
  });
73
- log(`${msg.with_agent_name}${shortId(msg.with_agent_id as string)} 接受了连接请求,会话 ID:${sid}`);
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
- `来自 ${partnerName}${shortId(msg.from_agent as string)}:${(msg.content as string).slice(0, 60)}`,
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("尚未注册,跳过 WS 连接");
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(`连接断开 (${code}),5s 后重连`);
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] 错误:", err.message);
157
+ console.error("[ClawSocial WS] Error:", err.message);
165
158
  });
166
159
  }
167
160
 
package/tsconfig.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "strict": true,
7
- "noEmit": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true
10
- },
11
- "include": ["**/*.ts"],
12
- "exclude": ["node_modules"]
13
- }