@wu529778790/open-im 1.8.3-beta.14 → 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,40 +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
- // Use the incoming session_id (WeChat KF compound chatId) so the server
160
- // can extract the external_userid for the WeChat KF send_msg touser.
161
- sessionId: payload.session_id,
168
+ sessionId: this.config.workspaceSessionId || sessionId,
162
169
  requestId: payload.prompt_id,
163
170
  state: payload.stop_reason === 'end_turn' ? 'completed' : payload.stop_reason,
164
171
  },
165
172
  };
166
173
  const url = `${this.config.httpBaseUrl}/v2/backgroundagent/wecom/local-proxy/receive`;
167
- log.debug(`${this.logPrefix} HTTP COPILOT_RESPONSE → ${url} chatId=${payload.session_id} msgLen=${message.length}`);
168
- fetch(url, {
169
- method: 'POST',
170
- headers: {
171
- 'Content-Type': 'application/json',
172
- Authorization: `Bearer ${this.config.httpAccessToken}`,
173
- },
174
- body: JSON.stringify(httpPayload),
175
- signal: AbortSignal.timeout(30_000),
176
- })
177
- .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
+ });
178
185
  const body = await res.text().catch(() => '');
179
186
  if (!res.ok) {
180
187
  log.error(`${this.logPrefix} HTTP COPILOT_RESPONSE failed: ${res.status} ${body.substring(0, 300)}`);
@@ -182,10 +189,10 @@ export class WorkBuddyCentrifugeClient {
182
189
  else {
183
190
  log.info(`${this.logPrefix} HTTP COPILOT_RESPONSE ok: ${res.status} ${body.substring(0, 200)}`);
184
191
  }
185
- })
186
- .catch((err) => {
192
+ }
193
+ catch (err) {
187
194
  log.error(`${this.logPrefix} HTTP COPILOT_RESPONSE error:`, err);
188
- });
195
+ }
189
196
  return;
190
197
  }
191
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.14",
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",