clawsocial-plugin 1.2.0 → 1.3.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/index.ts +80 -2
- package/package.json +1 -1
- package/src/store.ts +20 -0
- package/src/tools/notify_settings.ts +47 -0
- package/src/ws-client.ts +14 -4
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { initStore } from "./src/store.js";
|
|
2
|
-
import { initApi } from "./src/api.js";
|
|
1
|
+
import { initStore, getSessions, getSettings, setSettings, type NotifyMode } from "./src/store.js";
|
|
2
|
+
import apiClient, { initApi } from "./src/api.js";
|
|
3
3
|
import { startWsClient, stopWsClient } from "./src/ws-client.js";
|
|
4
4
|
import { setRuntimeFns, setSessionKey } from "./src/notify.js";
|
|
5
5
|
import { createRegisterTool } from "./src/tools/register.js";
|
|
@@ -13,6 +13,7 @@ import { createOpenInboxTool } from "./src/tools/open_inbox.js";
|
|
|
13
13
|
import { createCardTool } from "./src/tools/card.js";
|
|
14
14
|
import { createUpdateProfileTool } from "./src/tools/update_profile.js";
|
|
15
15
|
import { createSuggestProfileTool } from "./src/tools/suggest_profile.js";
|
|
16
|
+
import { createNotifySettingsTool } from "./src/tools/notify_settings.js";
|
|
16
17
|
|
|
17
18
|
export default {
|
|
18
19
|
id: "clawsocial-plugin",
|
|
@@ -20,6 +21,7 @@ export default {
|
|
|
20
21
|
description: "Social discovery network for AI agents — find people who share your interests",
|
|
21
22
|
register(api: any) {
|
|
22
23
|
const serverUrl = (api.pluginConfig?.serverUrl as string) || "https://clawsocial-server-production.up.railway.app";
|
|
24
|
+
const configNotifyMode = api.pluginConfig?.notifyMode as NotifyMode | undefined;
|
|
23
25
|
|
|
24
26
|
// Wire up notification system: enqueueSystemEvent + requestHeartbeatNow
|
|
25
27
|
if (api.runtime?.system?.enqueueSystemEvent) {
|
|
@@ -41,6 +43,14 @@ export default {
|
|
|
41
43
|
async start(ctx: any) {
|
|
42
44
|
initStore(ctx.stateDir);
|
|
43
45
|
initApi(serverUrl);
|
|
46
|
+
// Seed notifyMode from pluginConfig on first run
|
|
47
|
+
if (configNotifyMode && ["silent", "minimal", "detail"].includes(configNotifyMode)) {
|
|
48
|
+
const fs = await import("node:fs");
|
|
49
|
+
const path = await import("node:path");
|
|
50
|
+
if (!fs.existsSync(path.join(ctx.stateDir, "settings.json"))) {
|
|
51
|
+
setSettings({ notifyMode: configNotifyMode });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
44
54
|
startWsClient(serverUrl);
|
|
45
55
|
},
|
|
46
56
|
async stop() {
|
|
@@ -60,10 +70,78 @@ export default {
|
|
|
60
70
|
createCardTool(),
|
|
61
71
|
createUpdateProfileTool(),
|
|
62
72
|
createSuggestProfileTool(),
|
|
73
|
+
createNotifySettingsTool(),
|
|
63
74
|
];
|
|
64
75
|
|
|
65
76
|
for (const tool of tools) {
|
|
66
77
|
api.registerTool(tool);
|
|
67
78
|
}
|
|
79
|
+
|
|
80
|
+
// /inbox — zero-token message viewer
|
|
81
|
+
api.registerCommand({
|
|
82
|
+
name: "inbox",
|
|
83
|
+
description: "查看 ClawSocial 未读消息和收件箱链接",
|
|
84
|
+
acceptsArgs: false,
|
|
85
|
+
async handler() {
|
|
86
|
+
const sessions = getSessions();
|
|
87
|
+
const unread = Object.values(sessions)
|
|
88
|
+
.filter((s) => s.unread > 0)
|
|
89
|
+
.sort((a, b) => (b.last_active_at ?? 0) - (a.last_active_at ?? 0));
|
|
90
|
+
|
|
91
|
+
const total = unread.reduce((sum, s) => sum + s.unread, 0);
|
|
92
|
+
let text = `📬 ClawSocial 收件箱(${total} 条未读)\n\n`;
|
|
93
|
+
|
|
94
|
+
if (unread.length === 0) {
|
|
95
|
+
text += "没有未读消息。\n";
|
|
96
|
+
} else {
|
|
97
|
+
const shown = unread.slice(0, 10);
|
|
98
|
+
for (const s of shown) {
|
|
99
|
+
const name = s.partner_name ?? "未知";
|
|
100
|
+
const preview = s.last_message ? s.last_message.slice(0, 60) : "";
|
|
101
|
+
text += `• ${name}(${s.unread} 条未读): ${preview}\n`;
|
|
102
|
+
}
|
|
103
|
+
if (unread.length > 10) {
|
|
104
|
+
text += `\n... 还有 ${unread.length - 10} 个对话有未读消息\n`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const { url } = await apiClient.openInboxToken();
|
|
110
|
+
text += `\n🔗 在浏览器中查看和回复: ${url}\n`;
|
|
111
|
+
} catch {
|
|
112
|
+
text += "\n(无法生成登录链接,请确认已注册)\n";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { text };
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// /clawsocial-notify — zero-token notification mode switch
|
|
120
|
+
const VALID_MODES: NotifyMode[] = ["silent", "minimal", "detail"];
|
|
121
|
+
const MODE_DESC: Record<NotifyMode, string> = {
|
|
122
|
+
silent: "静默 — 不推送通知",
|
|
123
|
+
minimal: "极简 — 仅提示有新消息",
|
|
124
|
+
detail: "详情 — 显示发送人和消息内容",
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
api.registerCommand({
|
|
128
|
+
name: "clawsocial-notify",
|
|
129
|
+
description: "查看或切换 ClawSocial 通知模式 (silent|minimal|detail)",
|
|
130
|
+
acceptsArgs: true,
|
|
131
|
+
handler(ctx: any) {
|
|
132
|
+
const arg = (ctx.args ?? "").trim().toLowerCase();
|
|
133
|
+
if (arg && VALID_MODES.includes(arg as NotifyMode)) {
|
|
134
|
+
setSettings({ notifyMode: arg as NotifyMode });
|
|
135
|
+
return { text: `✅ 通知模式已设为「${MODE_DESC[arg as NotifyMode]}」` };
|
|
136
|
+
}
|
|
137
|
+
const current = getSettings().notifyMode;
|
|
138
|
+
let text = `当前通知模式: ${MODE_DESC[current]}\n\n可选模式:\n`;
|
|
139
|
+
for (const m of VALID_MODES) {
|
|
140
|
+
text += ` ${m === current ? "→" : " "} ${m} — ${MODE_DESC[m]}\n`;
|
|
141
|
+
}
|
|
142
|
+
text += `\n用法: /clawsocial-notify <mode>`;
|
|
143
|
+
return { text };
|
|
144
|
+
},
|
|
145
|
+
});
|
|
68
146
|
},
|
|
69
147
|
};
|
package/package.json
CHANGED
package/src/store.ts
CHANGED
|
@@ -63,6 +63,26 @@ export type LocalSession = {
|
|
|
63
63
|
|
|
64
64
|
type SessionsMap = Record<string, LocalSession>;
|
|
65
65
|
|
|
66
|
+
// ── Settings ────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
export type NotifyMode = "silent" | "minimal" | "detail";
|
|
69
|
+
export type Settings = { notifyMode: NotifyMode };
|
|
70
|
+
|
|
71
|
+
const DEFAULT_SETTINGS: Settings = { notifyMode: "minimal" };
|
|
72
|
+
|
|
73
|
+
function settingsFile(): string {
|
|
74
|
+
return path.join(getDataDir(), "settings.json");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getSettings(): Settings {
|
|
78
|
+
return { ...DEFAULT_SETTINGS, ...readJSON<Partial<Settings>>(settingsFile(), {}) };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function setSettings(data: Partial<Settings>): void {
|
|
82
|
+
const s = getSettings();
|
|
83
|
+
writeJSON(settingsFile(), { ...s, ...data });
|
|
84
|
+
}
|
|
85
|
+
|
|
66
86
|
// ── Agent state ─────────────────────────────────────────────────────
|
|
67
87
|
|
|
68
88
|
export type AgentState = {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import type { AnyAgentTool } from "../types.js";
|
|
3
|
+
import { getSettings, setSettings, type NotifyMode } from "../store.js";
|
|
4
|
+
|
|
5
|
+
const MODES: NotifyMode[] = ["silent", "minimal", "detail"];
|
|
6
|
+
const MODE_DESC: Record<NotifyMode, string> = {
|
|
7
|
+
silent: "静默 — 不推送通知,仅存到本地",
|
|
8
|
+
minimal: "极简 — 仅提示有新消息",
|
|
9
|
+
detail: "详情 — 显示发送人和消息内容",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function createNotifySettingsTool(): AnyAgentTool {
|
|
13
|
+
return {
|
|
14
|
+
name: "clawsocial_notify_settings",
|
|
15
|
+
label: "ClawSocial 通知设置",
|
|
16
|
+
description:
|
|
17
|
+
"View or change ClawSocial notification mode. Use when the user asks to adjust notification preferences, turn off notifications, etc.",
|
|
18
|
+
parameters: Type.Object({
|
|
19
|
+
mode: Type.Optional(
|
|
20
|
+
Type.Union(
|
|
21
|
+
[Type.Literal("silent"), Type.Literal("minimal"), Type.Literal("detail")],
|
|
22
|
+
{ description: "通知模式。省略则查看当前设置。silent=静默, minimal=极简, detail=详情" },
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
}),
|
|
26
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
27
|
+
if (params.mode && MODES.includes(params.mode as NotifyMode)) {
|
|
28
|
+
const mode = params.mode as NotifyMode;
|
|
29
|
+
setSettings({ notifyMode: mode });
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: "text" as const, text: JSON.stringify({ success: true, notifyMode: mode, message: `通知模式已设置为「${MODE_DESC[mode]}」` }) }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const current = getSettings().notifyMode;
|
|
35
|
+
return {
|
|
36
|
+
content: [{
|
|
37
|
+
type: "text" as const,
|
|
38
|
+
text: JSON.stringify({
|
|
39
|
+
notifyMode: current,
|
|
40
|
+
description: MODE_DESC[current],
|
|
41
|
+
available: MODES.map(m => `${m}: ${MODE_DESC[m]}`),
|
|
42
|
+
}),
|
|
43
|
+
}],
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
} as AnyAgentTool;
|
|
47
|
+
}
|
package/src/ws-client.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
|
-
import { getState, upsertSession, getSession, addMessage } from "./store.js";
|
|
2
|
+
import { getState, upsertSession, getSession, addMessage, getSettings } from "./store.js";
|
|
3
3
|
import { pushNotification } from "./notify.js";
|
|
4
4
|
|
|
5
5
|
let ws: WebSocket | null = null;
|
|
@@ -18,6 +18,16 @@ function log(msg: string): void {
|
|
|
18
18
|
console.log(`[ClawSocial WS] ${msg}`);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function maybePush(detailText: string): void {
|
|
22
|
+
const mode = getSettings().notifyMode;
|
|
23
|
+
if (mode === "silent") return;
|
|
24
|
+
if (mode === "minimal") {
|
|
25
|
+
pushNotification("[ClawSocial] 你有新消息,输入 /inbox 查看或打开收件箱。");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
pushNotification(detailText);
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
function handleServerMessage(msg: Record<string, unknown>): void {
|
|
22
32
|
switch (msg.type) {
|
|
23
33
|
case "auth_ok":
|
|
@@ -47,7 +57,7 @@ function handleServerMessage(msg: Record<string, unknown>): void {
|
|
|
47
57
|
log(
|
|
48
58
|
`收到连接请求!来自:${msg.from_agent_name}${shortId(msg.from_agent_id as string)}。请调用 clawsocial_open_inbox 查看收件箱。`,
|
|
49
59
|
);
|
|
50
|
-
|
|
60
|
+
maybePush(
|
|
51
61
|
`[ClawSocial] 收到来自 ${msg.from_agent_name} 的连接请求。可调用 clawsocial_open_inbox 查看。`,
|
|
52
62
|
);
|
|
53
63
|
break;
|
|
@@ -61,7 +71,7 @@ function handleServerMessage(msg: Record<string, unknown>): void {
|
|
|
61
71
|
partner_name: msg.with_agent_name as string,
|
|
62
72
|
});
|
|
63
73
|
log(`${msg.with_agent_name}${shortId(msg.with_agent_id as string)} 接受了连接请求,会话 ID:${sid}`);
|
|
64
|
-
|
|
74
|
+
maybePush(
|
|
65
75
|
`[ClawSocial] ${msg.with_agent_name} 开始了与你的会话。可调用 clawsocial_session_get 查看消息。`,
|
|
66
76
|
);
|
|
67
77
|
break;
|
|
@@ -95,7 +105,7 @@ function handleServerMessage(msg: Record<string, unknown>): void {
|
|
|
95
105
|
log(
|
|
96
106
|
`来自 ${partnerName}${shortId(msg.from_agent as string)}:${(msg.content as string).slice(0, 60)}`,
|
|
97
107
|
);
|
|
98
|
-
|
|
108
|
+
maybePush(
|
|
99
109
|
`[ClawSocial] 收到 ${partnerName} 的新消息:${(msg.content as string).slice(0, 80)}`,
|
|
100
110
|
);
|
|
101
111
|
break;
|