@wu529778790/open-im 1.8.3-beta.13 → 1.8.3-beta.15

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.
@@ -13,6 +13,13 @@ export interface CentrifugeClientConfig {
13
13
  httpBaseUrl?: string;
14
14
  httpAccessToken?: string;
15
15
  workspaceSessionId?: string;
16
+ /**
17
+ * Called before sending a WeChat KF reply to update the channel's channelId
18
+ * to the current WeChat user's externalUserId. The WorkBuddy server uses the
19
+ * registered channelId as the WeChat send_msg `touser`, so this must match the
20
+ * customer we are replying to.
21
+ */
22
+ registerChannelFn?: (externalUserId: string) => Promise<void>;
16
23
  }
17
24
  /** Client callbacks */
18
25
  export interface CentrifugeCallbacks {
@@ -58,7 +65,7 @@ export declare class WorkBuddyCentrifugeClient {
58
65
  /**
59
66
  * Send prompt response (for WeChat KF, use HTTP instead)
60
67
  */
61
- sendPromptResponse(payload: PromptResponsePayload, _guid?: string, _userId?: string): void;
68
+ sendPromptResponse(payload: PromptResponsePayload, _guid?: string, _userId?: string): Promise<void>;
62
69
  /**
63
70
  * Handle incoming publication from Centrifuge
64
71
  */
@@ -141,38 +141,47 @@ export class WorkBuddyCentrifugeClient {
141
141
  /**
142
142
  * Send prompt response (for WeChat KF, use HTTP instead)
143
143
  */
144
- sendPromptResponse(payload, _guid, _userId) {
144
+ async sendPromptResponse(payload, _guid, _userId) {
145
145
  // WeChat KF messages: send via HTTP COPILOT_RESPONSE
146
146
  if (this.config.httpBaseUrl && this.config.httpAccessToken) {
147
147
  const message = payload.content?.map((c) => c.text).join('') || payload.error || '';
148
- // Pass the full chatId as received from Centrifuge (e.g. "wmXXX::origin::wechatkfProxy").
149
- // The server identifies WeChat KF messages by the "::origin::wechatkfProxy" suffix
150
- // and extracts the external_userid from it to use as WeChat KF send_msg touser.
151
- const chatId = payload.session_id;
148
+ const sessionId = payload.session_id; // e.g. "wmXXX::origin::wechatkfProxy"
149
+ // The WorkBuddy server uses the registered channelId as the WeChat KF send_msg
150
+ // `touser`. Re-register the channel with the current WeChat user's externalUserId
151
+ // so that the server sends the reply to the correct customer.
152
+ if (this.config.registerChannelFn && sessionId.includes('::')) {
153
+ const externalUserId = sessionId.split('::')[0];
154
+ try {
155
+ await this.config.registerChannelFn(externalUserId);
156
+ }
157
+ catch (err) {
158
+ log.warn(`${this.logPrefix} registerChannelFn failed (reply may go to wrong user):`, err);
159
+ }
160
+ }
152
161
  const httpPayload = {
153
162
  type: 'COPILOT_RESPONSE',
154
163
  msgId: payload.prompt_id,
155
- chatId,
164
+ chatId: sessionId,
156
165
  success: payload.stop_reason === 'end_turn',
157
166
  message,
158
167
  metadata: {
159
- sessionId: this.config.workspaceSessionId || payload.session_id,
168
+ sessionId: this.config.workspaceSessionId || sessionId,
160
169
  requestId: payload.prompt_id,
161
170
  state: payload.stop_reason === 'end_turn' ? 'completed' : payload.stop_reason,
162
171
  },
163
172
  };
164
173
  const url = `${this.config.httpBaseUrl}/v2/backgroundagent/wecom/local-proxy/receive`;
165
- log.debug(`${this.logPrefix} HTTP COPILOT_RESPONSE → ${url} chatId=${payload.session_id} msgLen=${message.length}`);
166
- fetch(url, {
167
- method: 'POST',
168
- headers: {
169
- 'Content-Type': 'application/json',
170
- Authorization: `Bearer ${this.config.httpAccessToken}`,
171
- },
172
- body: JSON.stringify(httpPayload),
173
- signal: AbortSignal.timeout(30_000),
174
- })
175
- .then(async (res) => {
174
+ log.debug(`${this.logPrefix} HTTP COPILOT_RESPONSE → ${url} chatId=${sessionId} msgLen=${message.length}`);
175
+ try {
176
+ const res = await fetch(url, {
177
+ method: 'POST',
178
+ headers: {
179
+ 'Content-Type': 'application/json',
180
+ Authorization: `Bearer ${this.config.httpAccessToken}`,
181
+ },
182
+ body: JSON.stringify(httpPayload),
183
+ signal: AbortSignal.timeout(30_000),
184
+ });
176
185
  const body = await res.text().catch(() => '');
177
186
  if (!res.ok) {
178
187
  log.error(`${this.logPrefix} HTTP COPILOT_RESPONSE failed: ${res.status} ${body.substring(0, 300)}`);
@@ -180,10 +189,10 @@ export class WorkBuddyCentrifugeClient {
180
189
  else {
181
190
  log.info(`${this.logPrefix} HTTP COPILOT_RESPONSE ok: ${res.status} ${body.substring(0, 200)}`);
182
191
  }
183
- })
184
- .catch((err) => {
192
+ }
193
+ catch (err) {
185
194
  log.error(`${this.logPrefix} HTTP COPILOT_RESPONSE error:`, err);
186
- });
195
+ }
187
196
  return;
188
197
  }
189
198
  this.sendEnvelope('session.promptResponse', payload, _guid, _userId);
@@ -90,6 +90,18 @@ async function connect() {
90
90
  centrifugeClient.stop();
91
91
  centrifugeClient = null;
92
92
  }
93
+ // Re-registers the WeChat KF channel with the given externalUserId as channelId.
94
+ // The WorkBuddy server uses channelId as the WeChat send_msg `touser`, so this
95
+ // must be called with the customer's external_userid before sending each reply.
96
+ const registerChannelFn = async (externalUserId) => {
97
+ const clawSessionId = oauth.buildSessionId(clawPath);
98
+ await oauth.registerChannel({
99
+ type: 'wechatkf',
100
+ sessionId: clawSessionId,
101
+ channelId: externalUserId,
102
+ userId: pc.userId ?? '',
103
+ });
104
+ };
93
105
  centrifugeClient = new WorkBuddyCentrifugeClient({
94
106
  url: tokens.url,
95
107
  connectionToken: tokens.connectionToken,
@@ -100,6 +112,7 @@ async function connect() {
100
112
  httpBaseUrl: baseUrl,
101
113
  httpAccessToken: pc.accessToken ?? '',
102
114
  workspaceSessionId,
115
+ registerChannelFn,
103
116
  }, {
104
117
  onConnected: () => {
105
118
  log.info('WorkBuddy Centrifuge connected');
@@ -14,7 +14,7 @@ export async function sendTextReply(_client, chatId, text, msgId) {
14
14
  return;
15
15
  }
16
16
  log.info(`Sending WorkBuddy reply to chatId=${chatId}, msgId=${msgId}`);
17
- client.sendPromptResponse({
17
+ await client.sendPromptResponse({
18
18
  session_id: chatId,
19
19
  prompt_id: msgId,
20
20
  content: [{ type: 'text', text }],
@@ -31,7 +31,7 @@ export async function sendErrorReply(_client, chatId, error, msgId) {
31
31
  return;
32
32
  }
33
33
  log.info(`Sending WorkBuddy error to chatId=${chatId}, msgId=${msgId}`);
34
- client.sendPromptResponse({
34
+ await client.sendPromptResponse({
35
35
  session_id: chatId,
36
36
  prompt_id: msgId,
37
37
  error,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.8.3-beta.13",
3
+ "version": "1.8.3-beta.15",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, CodeBuddy)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",