palz-connector 1.4.6 → 1.4.8

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,7 +1,7 @@
1
1
  {
2
2
  "id": "palz-connector",
3
3
  "name": "Palz Connector Channel",
4
- "version": "1.4.6",
4
+ "version": "1.4.8",
5
5
  "description": "Palz IM 接入 OpenClaw",
6
6
  "channels": [
7
7
  "palz-connector"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palz-connector",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "description": "Palz IM 接入 OpenClaw — 模块化架构,基于 OpenClaw Runtime 消息管道",
package/src/bot.ts CHANGED
@@ -530,6 +530,7 @@ async function _dispatchPalzMessageInner(params: HandlePalzMessageParams): Promi
530
530
 
531
531
  // 构建 UntrustedContext:将 IM 消息的关键字段注入到 AI agent 的上下文中
532
532
  const untrustedContext: string[] = [
533
+ `msg_id: ${msg.msg_id}`,
533
534
  `sender_id: ${msg.sender_id}`,
534
535
  `sender_name: ${senderName}`,
535
536
  `conversation_id: ${msg.conversation_id}`,
@@ -557,6 +558,12 @@ async function _dispatchPalzMessageInner(params: HandlePalzMessageParams): Promi
557
558
  if (groupId) {
558
559
  untrustedContext.push(`group_id: ${groupId}`);
559
560
  }
561
+ if (msg.group_name) {
562
+ untrustedContext.push(`group_name: ${msg.group_name}`);
563
+ }
564
+ if (msg.group_owner_id) {
565
+ untrustedContext.push(`group_owner_id: ${msg.group_owner_id}`);
566
+ }
560
567
  if (msg.owner_id) {
561
568
  untrustedContext.push(`owner_id: ${msg.owner_id}`);
562
569
  }
@@ -580,7 +587,7 @@ async function _dispatchPalzMessageInner(params: HandlePalzMessageParams): Promi
580
587
  SessionKey: route.sessionKey,
581
588
  AccountId: route.accountId,
582
589
  ChatType: chatType,
583
- GroupSubject: isGroup ? msg.conversation_id : undefined,
590
+ GroupSubject: isGroup ? (msg.group_name || msg.conversation_id) : undefined,
584
591
  SenderId: msg.sender_id,
585
592
  SenderName: senderName,
586
593
  UntrustedContext: untrustedContext,
package/src/monitor.ts CHANGED
@@ -40,6 +40,7 @@ export async function monitorPalzProvider(params: MonitorPalzParams): Promise<vo
40
40
  let currentWs: WebSocket | null = null;
41
41
  let consecutive4002 = 0;
42
42
  const MAX_CONSECUTIVE_4002 = 10;
43
+ const MAX_RECONNECT_DELAY_MS = 16_000;
43
44
 
44
45
  const cleanup = () => {
45
46
  closed = true;
@@ -72,15 +73,28 @@ export async function monitorPalzProvider(params: MonitorPalzParams): Promise<vo
72
73
  }
73
74
  abortSignal?.addEventListener("abort", handleAbort, { once: true });
74
75
 
75
- function scheduleReconnect() {
76
+ function scheduleReconnect(delay = reconnectDelay, advanceBackoff = true) {
76
77
  if (closed) return;
77
- log(`palz[${accountId}]: scheduling reconnect in ${reconnectDelay}ms`);
78
- reconnectTimer = setTimeout(connect, reconnectDelay);
79
- reconnectDelay = Math.min(reconnectDelay * 2, 30_000);
78
+ if (reconnectTimer) {
79
+ clearTimeout(reconnectTimer);
80
+ reconnectTimer = null;
81
+ }
82
+ log(`palz[${accountId}]: scheduling reconnect in ${delay}ms`);
83
+ reconnectTimer = setTimeout(() => {
84
+ reconnectTimer = null;
85
+ connect();
86
+ }, delay);
87
+ if (advanceBackoff) {
88
+ reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
89
+ }
80
90
  }
81
91
 
82
92
  function connect() {
83
93
  if (closed) return;
94
+ if (reconnectTimer) {
95
+ clearTimeout(reconnectTimer);
96
+ reconnectTimer = null;
97
+ }
84
98
 
85
99
  let ws: WebSocket;
86
100
  try {
@@ -94,7 +108,55 @@ export async function monitorPalzProvider(params: MonitorPalzParams): Promise<vo
94
108
  let connectedAt = 0;
95
109
  let pingInterval: ReturnType<typeof setInterval> | null = null;
96
110
  let messageCount = 0;
97
- let closeFired = false;
111
+ let ended = false;
112
+
113
+ const handleSocketEnd = (code: number, reasonStr: string) => {
114
+ if (ended) return;
115
+ ended = true;
116
+ if (pingInterval) {
117
+ clearInterval(pingInterval);
118
+ pingInterval = null;
119
+ }
120
+ if (closed) return;
121
+
122
+ const wasConnected = connectedAt > 0;
123
+ const stableMs = wasConnected ? Date.now() - connectedAt : 0;
124
+
125
+ // 4002: 被新连接替代,带退避重试
126
+ if (code === 4002) {
127
+ consecutive4002++;
128
+ if (consecutive4002 >= MAX_CONSECUTIVE_4002) {
129
+ log(
130
+ `palz[${accountId}]: replaced ${consecutive4002} times (code=4002), giving up`,
131
+ );
132
+ cleanup();
133
+ resolve();
134
+ return;
135
+ }
136
+ const jitter = Math.floor(Math.random() * 2000);
137
+ const delay = 3000 * consecutive4002 + jitter;
138
+ log(
139
+ `palz[${accountId}]: replaced (code=4002, attempt=${consecutive4002}), retry in ${delay}ms`,
140
+ );
141
+ scheduleReconnect(delay, false);
142
+ return;
143
+ }
144
+
145
+ // 4001: bot_id 缺失,不重连
146
+ if (code === 4001) {
147
+ error(`palz[${accountId}]: bot_id missing (code=4001), not reconnecting`);
148
+ cleanup();
149
+ resolve();
150
+ return;
151
+ }
152
+
153
+ // 其他断开:指数退避重连
154
+ if (wasConnected && stableMs > 10_000) reconnectDelay = 1000;
155
+ log(
156
+ `palz[${accountId}]: disconnected (code=${code}, reason=${reasonStr}, uptime=${Math.round(stableMs / 1000)}s, msgs=${messageCount}), reconnecting in ${reconnectDelay}ms`,
157
+ );
158
+ scheduleReconnect();
159
+ };
98
160
 
99
161
  ws.on("open", () => {
100
162
  connectedAt = Date.now();
@@ -145,62 +207,12 @@ export async function monitorPalzProvider(params: MonitorPalzParams): Promise<vo
145
207
  });
146
208
 
147
209
  ws.on("close", (code: number, reason: Buffer) => {
148
- closeFired = true;
149
- if (pingInterval) {
150
- clearInterval(pingInterval);
151
- pingInterval = null;
152
- }
153
- if (closed) return;
154
-
155
- const stableMs = Date.now() - connectedAt;
156
- const reasonStr = reason?.toString() || "";
157
-
158
- // 4002: 被新连接替代,带退避重试
159
- if (code === 4002) {
160
- consecutive4002++;
161
- if (consecutive4002 >= MAX_CONSECUTIVE_4002) {
162
- log(
163
- `palz[${accountId}]: replaced ${consecutive4002} times (code=4002), giving up`,
164
- );
165
- cleanup();
166
- resolve();
167
- return;
168
- }
169
- const jitter = Math.floor(Math.random() * 2000);
170
- const delay = 3000 * consecutive4002 + jitter;
171
- log(
172
- `palz[${accountId}]: replaced (code=4002, attempt=${consecutive4002}), retry in ${delay}ms`,
173
- );
174
- reconnectTimer = setTimeout(connect, delay);
175
- return;
176
- }
177
-
178
- // 4001: bot_id 缺失,不重连
179
- if (code === 4001) {
180
- error(`palz[${accountId}]: bot_id missing (code=4001), not reconnecting`);
181
- cleanup();
182
- resolve();
183
- return;
184
- }
185
-
186
- // 其他断开:指数退避重连
187
- if (stableMs > 10_000) reconnectDelay = 1000;
188
- log(
189
- `palz[${accountId}]: disconnected (code=${code}, reason=${reasonStr}, uptime=${Math.round(stableMs / 1000)}s, msgs=${messageCount}), reconnecting in ${reconnectDelay}ms`,
190
- );
191
- scheduleReconnect();
210
+ handleSocketEnd(code, reason?.toString() || "");
192
211
  });
193
212
 
194
213
  ws.on("error", (err) => {
195
214
  error(`palz[${accountId}]: WebSocket error: ${err.message}`);
196
- if (!closeFired) {
197
- closeFired = true;
198
- if (pingInterval) {
199
- clearInterval(pingInterval);
200
- pingInterval = null;
201
- }
202
- scheduleReconnect();
203
- }
215
+ handleSocketEnd(1006, err.message);
204
216
  });
205
217
  }
206
218
 
package/src/types.ts CHANGED
@@ -37,6 +37,10 @@ export interface PalzMessageEvent {
37
37
  owner_name?: string;
38
38
  /** 群组 ID,群聊时 IM 可直接下发;若未提供则从 conversation_id 中解析 */
39
39
  group_id?: string;
40
+ /** 群主用户 ID,群聊时由 IM 下发 */
41
+ group_owner_id?: string;
42
+ /** 群聊名称,群聊时由 IM 下发 */
43
+ group_name?: string;
40
44
  /** Lobster ID,标识 agent 身份(IM 通过此字段区分不同 agent) */
41
45
  lobster_id?: string;
42
46
  /** 群组类型,由 IM 下发 */