@wu529778790/open-im 1.9.3-beta.0 → 1.9.3-beta.2
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/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
4
5
|
import { getConfiguredAiCommands, loadConfig, needsSetup, resolvePlatformAiCommand } from "./config.js";
|
|
5
6
|
import { runInteractiveSetup, runClaudeApiSetup } from "./setup.js";
|
|
@@ -22,6 +23,7 @@ import { initDingTalk, stopDingTalk, formatDingTalkInitError } from "./dingtalk/
|
|
|
22
23
|
import { setupDingTalkHandlers } from "./dingtalk/event-handler.js";
|
|
23
24
|
import { initWorkBuddy, stopWorkBuddy } from "./workbuddy/client.js";
|
|
24
25
|
import { setupWorkBuddyHandlers } from "./workbuddy/event-handler.js";
|
|
26
|
+
import { sendTextReply as sendWorkBuddyTextReply } from "./workbuddy/message-sender.js";
|
|
25
27
|
import { initAdapters, cleanupAdapters } from "./adapters/registry.js";
|
|
26
28
|
import { SessionManager } from "./session/session-manager.js";
|
|
27
29
|
import { loadActiveChats, getActiveChatId, flushActiveChats, } from "./shared/active-chats.js";
|
|
@@ -33,13 +35,14 @@ const require = createRequire(import.meta.url);
|
|
|
33
35
|
const { version: APP_VERSION } = require("../package.json");
|
|
34
36
|
const log = createLogger("Main");
|
|
35
37
|
async function sendLifecycleNotification(platform, message) {
|
|
36
|
-
// DingTalk
|
|
37
|
-
if (platform === "dingtalk"
|
|
38
|
+
// DingTalk 不支持主动发消息(OpenAPI 需 robotCode 等,易报 robot 不存在),跳过启动/关闭通知
|
|
39
|
+
if (platform === "dingtalk")
|
|
38
40
|
return;
|
|
39
41
|
const telegramChatId = getActiveChatId("telegram");
|
|
40
42
|
const feishuChatId = getActiveChatId("feishu");
|
|
41
43
|
const qqChatId = getActiveChatId("qq");
|
|
42
44
|
const weworkChatId = getActiveChatId("wework");
|
|
45
|
+
const workbuddyChatId = getActiveChatId("workbuddy");
|
|
43
46
|
const sendPromises = [];
|
|
44
47
|
if (platform === "telegram" && telegramChatId) {
|
|
45
48
|
sendPromises.push(sendTelegramTextReply(telegramChatId, message).catch((err) => {
|
|
@@ -61,6 +64,11 @@ async function sendLifecycleNotification(platform, message) {
|
|
|
61
64
|
log.debug("Failed to send WeWork notification:", err);
|
|
62
65
|
}));
|
|
63
66
|
}
|
|
67
|
+
if (platform === "workbuddy" && workbuddyChatId) {
|
|
68
|
+
sendPromises.push(sendWorkBuddyTextReply(null, workbuddyChatId, message, randomUUID()).catch((err) => {
|
|
69
|
+
log.debug("Failed to send WorkBuddy notification:", err);
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
64
72
|
await Promise.all(sendPromises);
|
|
65
73
|
}
|
|
66
74
|
function buildStartupMessage(platform, appVersion, aiCommand, defaultWorkDir, sessionManager) {
|
|
@@ -17,9 +17,14 @@ export interface CentrifugeClientConfig {
|
|
|
17
17
|
* Called before sending a WeChat KF reply to update the channel's channelId
|
|
18
18
|
* to the current WeChat user's externalUserId. The WorkBuddy server uses the
|
|
19
19
|
* registered channelId as the WeChat send_msg `touser`, so this must match the
|
|
20
|
-
* customer we are replying to.
|
|
20
|
+
* customer we are replying to. Also locks the heartbeat to prevent race conditions.
|
|
21
21
|
*/
|
|
22
22
|
registerChannelFn?: (externalUserId: string) => Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Called after the COPILOT_RESPONSE is sent (success or failure) to release
|
|
25
|
+
* the reply lock, allowing the heartbeat to resume.
|
|
26
|
+
*/
|
|
27
|
+
releaseChannelLockFn?: () => void;
|
|
23
28
|
}
|
|
24
29
|
/** Client callbacks */
|
|
25
30
|
export interface CentrifugeCallbacks {
|
|
@@ -193,6 +193,10 @@ export class WorkBuddyCentrifugeClient {
|
|
|
193
193
|
catch (err) {
|
|
194
194
|
log.error(`${this.logPrefix} HTTP COPILOT_RESPONSE error:`, err);
|
|
195
195
|
}
|
|
196
|
+
finally {
|
|
197
|
+
// Release the heartbeat lock so the periodic registration can resume
|
|
198
|
+
this.config.releaseChannelLockFn?.();
|
|
199
|
+
}
|
|
196
200
|
return;
|
|
197
201
|
}
|
|
198
202
|
this.sendEnvelope('session.promptResponse', payload, _guid, _userId);
|
package/dist/workbuddy/client.js
CHANGED
|
@@ -90,11 +90,16 @@ async function connect() {
|
|
|
90
90
|
centrifugeClient.stop();
|
|
91
91
|
centrifugeClient = null;
|
|
92
92
|
}
|
|
93
|
+
// Mutex flag: prevents the heartbeat from overriding channelId while a reply is in flight.
|
|
94
|
+
let replyLock = false;
|
|
93
95
|
// Re-registers the WeChat KF channel with the given externalUserId as channelId.
|
|
94
96
|
// The WorkBuddy server uses channelId as the WeChat send_msg `touser`, so this
|
|
95
97
|
// must be called with the customer's external_userid before sending each reply.
|
|
98
|
+
// Sets replyLock=true to block the heartbeat from overriding while we send.
|
|
96
99
|
const registerChannelFn = async (externalUserId) => {
|
|
100
|
+
replyLock = true;
|
|
97
101
|
const clawSessionId = oauth.buildSessionId(clawPath);
|
|
102
|
+
log.debug(`registerChannelFn: registering channelId=${externalUserId} (heartbeat paused)`);
|
|
98
103
|
await oauth.registerChannel({
|
|
99
104
|
type: 'wechatkf',
|
|
100
105
|
sessionId: clawSessionId,
|
|
@@ -102,6 +107,10 @@ async function connect() {
|
|
|
102
107
|
userId: pc.userId ?? '',
|
|
103
108
|
});
|
|
104
109
|
};
|
|
110
|
+
const releaseChannelLockFn = () => {
|
|
111
|
+
replyLock = false;
|
|
112
|
+
log.debug('registerChannelFn: reply sent, heartbeat resumed');
|
|
113
|
+
};
|
|
105
114
|
centrifugeClient = new WorkBuddyCentrifugeClient({
|
|
106
115
|
url: tokens.url,
|
|
107
116
|
connectionToken: tokens.connectionToken,
|
|
@@ -113,6 +122,7 @@ async function connect() {
|
|
|
113
122
|
httpAccessToken: pc.accessToken ?? '',
|
|
114
123
|
workspaceSessionId,
|
|
115
124
|
registerChannelFn,
|
|
125
|
+
releaseChannelLockFn,
|
|
116
126
|
}, {
|
|
117
127
|
onConnected: () => {
|
|
118
128
|
log.info('WorkBuddy Centrifuge connected');
|
|
@@ -133,6 +143,10 @@ async function connect() {
|
|
|
133
143
|
const doRegister = () => {
|
|
134
144
|
if (stopped || channelState !== 'connected')
|
|
135
145
|
return;
|
|
146
|
+
if (replyLock) {
|
|
147
|
+
log.debug('Heartbeat skipped (reply in progress)');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
136
150
|
oauth.registerChannel({
|
|
137
151
|
type: 'wechatkf',
|
|
138
152
|
sessionId: clawSessionId,
|
|
@@ -152,6 +166,10 @@ async function connect() {
|
|
|
152
166
|
const doRegister = () => {
|
|
153
167
|
if (stopped || channelState !== 'connected')
|
|
154
168
|
return;
|
|
169
|
+
if (replyLock) {
|
|
170
|
+
log.debug('Heartbeat skipped (reply in progress, fallback path)');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
155
173
|
oauth.registerChannel({
|
|
156
174
|
type: 'wechatkf',
|
|
157
175
|
sessionId: workspaceSessionId,
|