@hywkp/sider 0.0.4 → 0.0.6

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,280 @@
1
+ # Sider OpenClaw Plugin 对接文档
2
+
3
+ 本文档面向两类同学:
4
+ - 需要安装 `openclaw-plugins/sider` 的运维/后端同学
5
+ - 需要消费 `typing/stream/tool` 事件的客户端同学(Web/iOS/Android)
6
+
7
+ ---
8
+
9
+ ## 1. 一键安装
10
+
11
+ 安装脚本默认执行:
12
+ - `openclaw plugins install @hywkp/sider`
13
+ - 若已安装(`plugin already exists`),自动尝试:`openclaw plugins update sider`
14
+
15
+ 仅安装插件(不改配置):
16
+
17
+ ```bash
18
+ curl -fsSL https://chat-demo.hyw.workers.dev/sider/install-openclaw-plugin.sh | RUN_CONFIGURE=0 bash
19
+ ```
20
+
21
+ 安装并写入 `channels.sider` 配置:
22
+
23
+ ```bash
24
+ curl -fsSL https://chat-demo.hyw.workers.dev/sider/install-openclaw-plugin.sh | \
25
+ SIDER_GATEWAY_URL='http://127.0.0.1:8080' \
26
+ SIDER_SESSION_ID='s1' \
27
+ bash
28
+ ```
29
+
30
+ 可选参数:
31
+ - `SIDER_RELAY_ID`
32
+ - `SIDER_RELAY_TOKEN`
33
+ - 兼容旧变量:`SIDER_SESSION_KEY`(会自动映射到 `SIDER_SESSION_ID`)
34
+
35
+ ---
36
+
37
+ ## 2. OpenClaw 配置示例
38
+
39
+ `~/.openclaw/openclaw.json`:
40
+
41
+ ```json
42
+ {
43
+ "channels": {
44
+ "sider": {
45
+ "enabled": true,
46
+ "gatewayUrl": "http://127.0.0.1:8080",
47
+ "sessionId": "s1",
48
+ "defaultTo": "session:s1",
49
+ "relayId": "openclaw-default"
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ 检查状态:
56
+
57
+ ```bash
58
+ openclaw channels list
59
+ openclaw status --json
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 3. 基础协议(relay 侧)
65
+
66
+ WebSocket 连接:
67
+ - `ws://<gateway>/ws/relay`
68
+
69
+ 握手首帧:
70
+
71
+ ```json
72
+ {"type":"register","session_id":"<session_id>","relay_id":"<relay_id>","token":"<optional>"}
73
+ ```
74
+
75
+ 发送持久化消息:
76
+
77
+ ```json
78
+ {
79
+ "type":"message",
80
+ "session_id":"<session_id>",
81
+ "client_req_id":"<uuid>",
82
+ "parts":[{"type":"core.text","payload":{"text":"hello"}}]
83
+ }
84
+ ```
85
+
86
+ 发送实时事件:
87
+
88
+ ```json
89
+ {
90
+ "type":"event",
91
+ "session_id":"<session_id>",
92
+ "client_req_id":"<uuid>",
93
+ "event_type":"typing",
94
+ "payload":{"on":true},
95
+ "meta":{}
96
+ }
97
+ ```
98
+
99
+ ACK:
100
+
101
+ ```json
102
+ {"type":"ack","session_id":"<session_id>","id":"<id>"}
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 4. 客户端事件约定(typing / stream / tool)
108
+
109
+ 插件会发送以下实时事件(`type=event`,`source_role=relay`)。
110
+
111
+ ### 4.1 typing
112
+
113
+ 开始输入:
114
+
115
+ ```json
116
+ {
117
+ "event_type":"typing",
118
+ "payload":{
119
+ "on":true,
120
+ "state":"typing",
121
+ "session_id":"<session_id>",
122
+ "ts":1730000000000
123
+ },
124
+ "meta":{
125
+ "channel":"sider",
126
+ "account_id":"default",
127
+ "schema_version":1
128
+ }
129
+ }
130
+ ```
131
+
132
+ 停止输入时:`on=false`,`state=idle`。
133
+
134
+ ### 4.2 stream.start
135
+
136
+ ```json
137
+ {
138
+ "event_type":"stream.start",
139
+ "payload":{
140
+ "session_id":"<session_id>",
141
+ "stream_id":"<uuid>",
142
+ "ts":1730000000000
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### 4.3 stream.delta
148
+
149
+ ```json
150
+ {
151
+ "event_type":"stream.delta",
152
+ "payload":{
153
+ "session_id":"<session_id>",
154
+ "stream_id":"<uuid>",
155
+ "seq":1,
156
+ "delta":"你好,",
157
+ "done":false,
158
+ "chunk_chars":3,
159
+ "ts":1730000000001
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### 4.4 stream.done
165
+
166
+ ```json
167
+ {
168
+ "event_type":"stream.done",
169
+ "payload":{
170
+ "session_id":"<session_id>",
171
+ "stream_id":"<uuid>",
172
+ "seq":99,
173
+ "done":true,
174
+ "reason":"final|interrupted",
175
+ "ts":1730000000999
176
+ }
177
+ }
178
+ ```
179
+
180
+ 说明:
181
+ - `reason=final`:流式正常结束
182
+ - `reason=interrupted`:流式被中断(例如上游出错或中断)
183
+
184
+ ### 4.5 tool.call
185
+
186
+ 工具调用开始事件(来源于 OpenClaw `before_tool_call`):
187
+
188
+ ```json
189
+ {
190
+ "event_type":"tool.call",
191
+ "payload":{
192
+ "session_id":"<session_id>",
193
+ "seq":1,
194
+ "call_id":"<uuid>",
195
+ "phase":"start",
196
+ "tool_name":"read",
197
+ "tool_call_id":"<provider_call_id>",
198
+ "run_id":"<run_id>",
199
+ "session_key":"agent:main:...",
200
+ "tool_args":{"path":"README.md"},
201
+ "error":null,
202
+ "duration_ms":null,
203
+ "ts":1730000000002
204
+ },
205
+ "meta":{
206
+ "channel":"sider",
207
+ "account_id":"default",
208
+ "schema_version":1
209
+ }
210
+ }
211
+ ```
212
+
213
+ 说明:
214
+ - `call_id` 用于把同一次工具调用的 `tool.call` 和 `tool.result` 关联起来。
215
+ - `tool_args` 即工具参数(例如 `read` 的 `path`)。
216
+ - `tool_call_id` 为 provider 侧 call id(有则透传)。
217
+
218
+ ### 4.6 tool.result
219
+
220
+ 工具结果事件(来源于 OpenClaw `after_tool_call`):
221
+
222
+ ```json
223
+ {
224
+ "event_type":"tool.result",
225
+ "payload":{
226
+ "session_id":"<session_id>",
227
+ "seq":2,
228
+ "call_id":"<uuid>",
229
+ "tool_name":"read",
230
+ "tool_call_id":"<provider_call_id>",
231
+ "run_id":"<run_id>",
232
+ "session_key":"agent:main:...",
233
+ "tool_args":{"path":"README.md"},
234
+ "result":{"content":[{"type":"text","text":"..."}]},
235
+ "error":null,
236
+ "duration_ms":32,
237
+ "text":"",
238
+ "has_text":false,
239
+ "media_urls":[],
240
+ "media_count":0,
241
+ "is_error":false,
242
+ "ts":1730000000003
243
+ }
244
+ }
245
+ ```
246
+
247
+ 说明:
248
+ - `result` 为工具返回值(JSON-safe 序列化后)。
249
+ - `error` 非空时表示工具执行失败。
250
+ - `text` 为插件提取的“可读摘要”,仅从 `result.text/content/message/output/stdout` 取值,不做回退。
251
+ - `tool.result` 是工具阶段的实时事件,不替代最终 `type=message`。
252
+
253
+ ---
254
+
255
+ ## 5. 客户端渲染建议
256
+
257
+ - 使用 `stream_id + seq` 作为流式拼接键,按 `seq` 递增拼接 `delta`
258
+ - `stream.*` 仅用于实时渲染,不作为历史消息源
259
+ - `tool.call/tool.result` 仅用于实时状态,不作为历史消息源
260
+ - 最终以 `type=message` 的持久化消息为准(用于会话历史)
261
+ - 收到 `stream.done` 后,等待最终 `message` 到达再收敛 UI
262
+
263
+ ---
264
+
265
+ ## 6. 常见排查
266
+
267
+ - 看不到 typing/stream:
268
+ - 检查是否连接到了同一个 `session_id`
269
+ - 检查客户端是否处理了 `type=event`
270
+ - 收到附件但模型像“看不到文件”:
271
+ - 插件入站会先下载 `core.file/core.media` 到本地媒体存储,再把 `MediaPath(s)` 放入 OpenClaw 上下文
272
+ - 若下载失败,会降级为把附件 URL 拼进消息正文(日志关键字:`sider inbound media download failed`)
273
+ - 图片/文件发送失败:
274
+ - 插件会走 `POST /v1/files/init` + 直传 `upload_url` + `POST /v1/files/complete(可选)`
275
+ - 若网关返回 `501 file service not enabled`,说明当前不是 S3 文件存储驱动
276
+ - 发送成功但没收到最终回复:
277
+ - 检查是否收到 `ack`
278
+ - 检查 OpenClaw 日志中的 `sider` 相关错误
279
+ - 收到 `replaced`:
280
+ - 表示同 `relay_id` 有新连接顶掉旧连接,需重连
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,QAAA,MAAM,MAAM;;;;;kBAKI,iBAAiB;CAIhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,QAAA,MAAM,MAAM;;;;;kBAKI,iBAAiB;CA2BhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
2
- import { setSiderRuntime, siderPlugin } from "./src/channel.js";
2
+ import { emitSiderToolHookEvent, setSiderRuntime, siderPlugin } from "./src/channel.js";
3
3
  const plugin = {
4
4
  id: "sider",
5
5
  name: "Sider",
@@ -8,6 +8,29 @@ const plugin = {
8
8
  register(api) {
9
9
  setSiderRuntime(api.runtime);
10
10
  api.registerChannel({ plugin: siderPlugin });
11
+ api.on("before_tool_call", async (event, ctx) => {
12
+ await emitSiderToolHookEvent({
13
+ sessionKey: ctx.sessionKey,
14
+ phase: "start",
15
+ toolName: event.toolName ?? ctx.toolName,
16
+ toolCallId: event.toolCallId ?? ctx.toolCallId,
17
+ runId: event.runId ?? ctx.runId,
18
+ params: event.params,
19
+ });
20
+ });
21
+ api.on("after_tool_call", async (event, ctx) => {
22
+ await emitSiderToolHookEvent({
23
+ sessionKey: ctx.sessionKey,
24
+ phase: event.error ? "error" : "end",
25
+ toolName: event.toolName ?? ctx.toolName,
26
+ toolCallId: event.toolCallId ?? ctx.toolCallId,
27
+ runId: event.runId ?? ctx.runId,
28
+ params: event.params,
29
+ result: event.result,
30
+ error: event.error,
31
+ durationMs: event.durationMs,
32
+ });
33
+ });
11
34
  },
12
35
  };
13
36
  export default plugin;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,MAAM,GAAG;IACb,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,OAAO;IACb,WAAW,EAAE,uCAAuC;IACpD,YAAY,EAAE,uBAAuB,EAAE;IACvC,QAAQ,CAAC,GAAsB;QAC7B,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IAC/C,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAExF,MAAM,MAAM,GAAG;IACb,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,OAAO;IACb,WAAW,EAAE,uCAAuC;IACpD,YAAY,EAAE,uBAAuB,EAAE;IACvC,QAAQ,CAAC,GAAsB;QAC7B,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,GAAG,CAAC,EAAE,CAAC,kBAAkB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC9C,MAAM,sBAAsB,CAAC;gBAC3B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,KAAK,EAAE,OAAO;gBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;gBACxC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU;gBAC9C,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7C,MAAM,sBAAsB,CAAC;gBAC3B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;gBACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;gBACxC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU;gBAC9C,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -27,7 +27,19 @@ type ResolvedSiderAccount = {
27
27
  configured: boolean;
28
28
  config: SiderAccountConfig;
29
29
  };
30
+ type SiderToolHookPayload = {
31
+ sessionKey?: string;
32
+ phase: "start" | "end" | "error";
33
+ toolName?: string;
34
+ toolCallId?: string;
35
+ runId?: string;
36
+ params?: Record<string, unknown>;
37
+ result?: unknown;
38
+ error?: string;
39
+ durationMs?: number;
40
+ };
30
41
  export declare function setSiderRuntime(runtime: PluginRuntime): void;
42
+ export declare function emitSiderToolHookEvent(params: SiderToolHookPayload): Promise<void>;
31
43
  export declare const siderPlugin: ChannelPlugin<ResolvedSiderAccount>;
32
44
  export {};
33
45
  //# sourceMappingURL=channel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,aAAa,EAElB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAC;AAQ7B,KAAK,kBAAkB,GAAG;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAMF,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,kBAAkB,CAAC;CAC5B,CAAC;AAgIF,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAE5D;AAy4CD,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,oBAAoB,CAsJ3D,CAAC"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAWL,KAAK,aAAa,EAElB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAC;AAW7B,KAAK,kBAAkB,GAAG;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAMF,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,kBAAkB,CAAC;CAC5B,CAAC;AA+DF,KAAK,oBAAoB,GAAG;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA8CF,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAE5D;AA42BD,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqExF;AA8yBD,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,oBAAoB,CAkP3D,CAAC"}