clawpet-plugins 1.0.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 ADDED
@@ -0,0 +1,36 @@
1
+ # ClawPet Plugin for OpenClaw 🦞
2
+
3
+ 自动上报 Agent tool call 到 ClawPet,驱动龙虾实时动画。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ openclaw plugins install clawpet-plugins
9
+ ```
10
+
11
+ ## 配置
12
+
13
+ 在 OpenClaw 配置中设置 apiKey:
14
+
15
+ ```json
16
+ {
17
+ "plugins": {
18
+ "entries": {
19
+ "clawpet-plugins": {
20
+ "enabled": true,
21
+ "config": {
22
+ "apiKey": "你的 ClawPet API Key"
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ 重启 gateway 生效。
31
+
32
+ ## 功能
33
+
34
+ - 每次 tool call 自动上报
35
+ - 异步执行,不影响 agent 性能
36
+ - 失败静默,不中断正常工作
package/index.ts ADDED
@@ -0,0 +1,184 @@
1
+ /**
2
+ * ClawPet OpenClaw Plugin
3
+ *
4
+ * 自动上报 agent 的 tool call 到 ClawPet 后端,
5
+ * 驱动龙虾宠物实时动画。
6
+ *
7
+ * Hook:
8
+ * - after_tool_call: 每次 tool 执行完毕后上报 action + summary
9
+ * - agent_end: 每轮对话结束时上报
10
+ *
11
+ * 配置(在 OpenClaw config 的 plugins.entries 段):
12
+ * {
13
+ * "clawpet-plugins": {
14
+ * "enabled": true,
15
+ * "config": {
16
+ * "apiKey": "cpk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
17
+ * "apiBase": "https://api.venusx.top"
18
+ * }
19
+ * }
20
+ * }
21
+ */
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // 类型定义(对齐 OpenClaw Plugin API,不依赖源码 import)
25
+ // ---------------------------------------------------------------------------
26
+
27
+ type PluginLogger = {
28
+ debug?: (message: string) => void;
29
+ info: (message: string) => void;
30
+ warn: (message: string) => void;
31
+ error: (message: string) => void;
32
+ };
33
+
34
+ type PluginApi = {
35
+ id: string;
36
+ name: string;
37
+ pluginConfig?: Record<string, unknown>;
38
+ logger: PluginLogger;
39
+ on: (
40
+ hookName: string,
41
+ handler: (event: any, ctx: any) => void | Promise<void>,
42
+ opts?: { priority?: number },
43
+ ) => void;
44
+ };
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // 常量
48
+ // ---------------------------------------------------------------------------
49
+
50
+ /** 默认 API 地址 */
51
+ const DEFAULT_API_BASE = "https://api.venusx.top";
52
+
53
+ /** 去重窗口(ms)—— 同一工具 2 秒内不重复上报 */
54
+ const DEDUP_WINDOW = 2000;
55
+
56
+ /** 上报超时(ms) */
57
+ const REPORT_TIMEOUT = 5000;
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // 插件入口
61
+ // ---------------------------------------------------------------------------
62
+
63
+ export default {
64
+ register(api: PluginApi) {
65
+ const config = api.pluginConfig as
66
+ | { apiKey?: string; apiBase?: string }
67
+ | undefined;
68
+ const apiKey = config?.apiKey;
69
+ let apiBase = config?.apiBase || DEFAULT_API_BASE;
70
+
71
+ if (!apiKey) {
72
+ api.logger.warn(
73
+ "[ClawPet] No apiKey configured — plugin disabled. " +
74
+ "Set plugins.entries.clawpet-plugins.config.apiKey in your OpenClaw config.",
75
+ );
76
+ return;
77
+ }
78
+
79
+ if (!apiKey.startsWith("cpk_")) {
80
+ api.logger.warn(
81
+ "[ClawPet] apiKey should start with 'cpk_' — check your config.",
82
+ );
83
+ return;
84
+ }
85
+
86
+ api.logger.info(`[ClawPet] Plugin active, reporting to ${apiBase}`);
87
+
88
+ // 去重:记录最近上报的 toolName → timestamp
89
+ const lastReported = new Map<string, number>();
90
+
91
+ /**
92
+ * 异步上报到 ClawPet 后端
93
+ * 不 await,不阻塞 agent
94
+ */
95
+ function reportActivity(action: string, summary: string): void {
96
+ // 去重检查
97
+ const now = Date.now();
98
+ const lastTime = lastReported.get(action);
99
+ if (lastTime && now - lastTime < DEDUP_WINDOW) return;
100
+ lastReported.set(action, now);
101
+
102
+ // 清理过期去重记录(防内存泄漏)
103
+ if (lastReported.size > 100) {
104
+ for (const [key, ts] of lastReported) {
105
+ if (now - ts > DEDUP_WINDOW * 5) lastReported.delete(key);
106
+ }
107
+ }
108
+
109
+ // 异步 POST,fire-and-forget
110
+ const controller = new AbortController();
111
+ const timeout = setTimeout(() => controller.abort(), REPORT_TIMEOUT);
112
+
113
+ fetch(`${apiBase}/webhook`, {
114
+ method: "POST",
115
+ headers: {
116
+ "Content-Type": "application/json",
117
+ Authorization: `Bearer ${apiKey}`,
118
+ },
119
+ body: JSON.stringify({ action, summary }),
120
+ signal: controller.signal,
121
+ })
122
+ .then(async (res) => {
123
+ clearTimeout(timeout);
124
+ if (res.ok) {
125
+ try {
126
+ const data = await res.json();
127
+ // 搭便车更新 apiBase
128
+ if (data?.config?.apiBase && data.config.apiBase !== apiBase) {
129
+ api.logger.info(
130
+ `[ClawPet] API base updated: ${apiBase} → ${data.config.apiBase}`,
131
+ );
132
+ apiBase = data.config.apiBase;
133
+ }
134
+ } catch {
135
+ // JSON 解析失败,忽略
136
+ }
137
+ }
138
+ })
139
+ .catch(() => {
140
+ clearTimeout(timeout);
141
+ // 上报失败静默忽略,不影响 agent 工作
142
+ });
143
+ }
144
+
145
+ // ========================================
146
+ // Hook: after_tool_call
147
+ // 每次 tool 执行完毕后上报
148
+ // ========================================
149
+
150
+ api.on("after_tool_call", (event: any) => {
151
+ try {
152
+ if (!event) return;
153
+ const { toolName, durationMs, error } = event;
154
+ if (!toolName) return;
155
+
156
+ const summary = error
157
+ ? `${toolName} failed (${durationMs ?? 0}ms)`
158
+ : `${toolName} (${durationMs ?? 0}ms)`;
159
+
160
+ reportActivity(toolName, summary);
161
+ } catch {
162
+ // Hook 内部错误静默,绝不影响 agent
163
+ }
164
+ });
165
+
166
+ // ========================================
167
+ // Hook: agent_end
168
+ // Agent 一轮结束时上报
169
+ // ========================================
170
+
171
+ api.on("agent_end", (event: any) => {
172
+ try {
173
+ if (!event) return;
174
+ if (event.success) {
175
+ reportActivity("idle", `Turn complete (${event.durationMs ?? 0}ms)`);
176
+ } else {
177
+ reportActivity("error", `Turn failed: ${event.error ?? "unknown"}`);
178
+ }
179
+ } catch {
180
+ // Hook 内部错误静默,绝不影响 agent
181
+ }
182
+ });
183
+ },
184
+ };
@@ -0,0 +1,21 @@
1
+ {
2
+ "id": "clawpet-plugins",
3
+ "name": "ClawPet",
4
+ "description": "Auto-report agent tool calls to ClawPet for real-time lobster visualization.",
5
+ "configSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "apiBase": {
10
+ "type": "string",
11
+ "description": "ClawPet API base URL",
12
+ "default": "https://api.venusx.top"
13
+ },
14
+ "apiKey": {
15
+ "type": "string",
16
+ "description": "ClawPet API key (cpk_xxx)"
17
+ }
18
+ },
19
+ "required": []
20
+ }
21
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "clawpet-plugins",
3
+ "version": "1.0.0",
4
+ "description": "OpenClaw plugin for ClawPet — auto-report tool calls for real-time pet visualization",
5
+ "type": "module",
6
+ "main": "index.ts",
7
+ "license": "MIT",
8
+ "keywords": ["openclaw", "openclaw-plugin", "clawpet", "pet", "visualization"],
9
+ "files": [
10
+ "index.ts",
11
+ "openclaw.plugin.json",
12
+ "README.md"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/ronin-storm/claw-pet",
17
+ "directory": "plugin"
18
+ },
19
+ "homepage": "https://myclawpet.com",
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "openclaw": {
24
+ "extensions": [
25
+ "./index.ts"
26
+ ]
27
+ }
28
+ }