@wu529778790/open-im 1.8.3-beta.7 → 1.8.3-beta.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.
package/dist/setup.js
CHANGED
|
@@ -460,6 +460,10 @@ export async function runInteractiveSetup() {
|
|
|
460
460
|
// account info is optional
|
|
461
461
|
}
|
|
462
462
|
const userId = accountInfo?.uid?.toString() ?? "";
|
|
463
|
+
// Set oauth.userId so buildSessionId() produces the correct sessionId.
|
|
464
|
+
// Without this, the binding sessionId has an empty userId prefix, which
|
|
465
|
+
// won't match the sessionId the transport uses at runtime.
|
|
466
|
+
oauth.userId = userId;
|
|
463
467
|
config.platforms.wechat = {
|
|
464
468
|
enabled: true,
|
|
465
469
|
workbuddyAccessToken: tokenResult.accessToken,
|
|
@@ -20,10 +20,16 @@ export declare class WorkBuddyTransport implements WeChatTransport {
|
|
|
20
20
|
private state;
|
|
21
21
|
private centrifugeClient;
|
|
22
22
|
private oauth;
|
|
23
|
+
private stopped;
|
|
24
|
+
private reconnectAttempt;
|
|
25
|
+
private reconnectTimer;
|
|
26
|
+
private heartbeatTimer;
|
|
23
27
|
private messageHandler;
|
|
24
28
|
private stateChangeHandler;
|
|
25
29
|
constructor(config: WorkBuddyTransportConfig);
|
|
26
30
|
start(): Promise<void>;
|
|
31
|
+
private connect;
|
|
32
|
+
private scheduleReconnect;
|
|
27
33
|
stop(): void;
|
|
28
34
|
send(method: string, payload: unknown, replyTo?: string): void;
|
|
29
35
|
onMessage(handler: MessageHandler): void;
|
|
@@ -12,28 +12,43 @@ import { createLogger } from '../logger.js';
|
|
|
12
12
|
import { WorkBuddyCentrifugeClient } from '../workbuddy/centrifuge-client.js';
|
|
13
13
|
import { WorkBuddyOAuth } from '../workbuddy/oauth.js';
|
|
14
14
|
const log = createLogger('WeChat:WorkBuddy');
|
|
15
|
+
const RECONNECT_DELAYS_MS = [3000, 5000, 10000, 20000, 30000];
|
|
16
|
+
const CHANNEL_HEARTBEAT_MS = 30_000;
|
|
15
17
|
export class WorkBuddyTransport {
|
|
16
18
|
config;
|
|
17
19
|
state = 'disconnected';
|
|
18
20
|
centrifugeClient = null;
|
|
19
21
|
oauth = null;
|
|
22
|
+
stopped = false;
|
|
23
|
+
reconnectAttempt = 0;
|
|
24
|
+
reconnectTimer = null;
|
|
25
|
+
heartbeatTimer = null;
|
|
20
26
|
messageHandler = null;
|
|
21
27
|
stateChangeHandler = null;
|
|
22
28
|
constructor(config) {
|
|
23
29
|
this.config = config;
|
|
24
30
|
}
|
|
25
31
|
async start() {
|
|
32
|
+
this.stopped = false;
|
|
33
|
+
await this.connect();
|
|
34
|
+
}
|
|
35
|
+
async connect() {
|
|
36
|
+
if (this.stopped)
|
|
37
|
+
return;
|
|
26
38
|
this.updateState('connecting');
|
|
27
39
|
try {
|
|
28
|
-
// Initialize OAuth
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
// Initialize (or reuse) OAuth
|
|
41
|
+
if (!this.oauth) {
|
|
42
|
+
this.oauth = new WorkBuddyOAuth(this.config.baseUrl ?? 'https://copilot.tencent.com');
|
|
43
|
+
this.oauth.accessToken = this.config.accessToken;
|
|
44
|
+
this.oauth.refreshToken = this.config.refreshToken;
|
|
45
|
+
this.oauth.userId = this.config.userId;
|
|
46
|
+
}
|
|
47
|
+
// Use a stable workspaceId so the server maintains a consistent channel
|
|
48
|
+
// across restarts — otherwise WeChat KF loses the routing association.
|
|
35
49
|
const hostId = this.config.hostId ?? hostname();
|
|
36
|
-
const workspaceId =
|
|
50
|
+
const workspaceId = `${hostId}-open-im-wechat`;
|
|
51
|
+
log.info('Registering workspace for Centrifuge tokens...');
|
|
37
52
|
const tokens = await this.oauth.registerWorkspace({
|
|
38
53
|
userId: this.config.userId,
|
|
39
54
|
hostId,
|
|
@@ -41,15 +56,21 @@ export class WorkBuddyTransport {
|
|
|
41
56
|
workspaceName: 'open-im-wechat',
|
|
42
57
|
});
|
|
43
58
|
log.info(`Workspace registered: channel=${tokens.channel}`);
|
|
44
|
-
//
|
|
59
|
+
// workspaceSessionId must match the sessionId used when the WeChat KF was bound
|
|
45
60
|
const workspacePath = this.config.workspacePath ?? join(homedir(), 'WorkBuddy', 'Claw');
|
|
46
61
|
const workspaceSessionId = `${this.config.userId}_${hostId}_${workspacePath}`;
|
|
47
|
-
|
|
62
|
+
const channel = tokens.channel;
|
|
63
|
+
const oauth = this.oauth;
|
|
64
|
+
// Tear down previous client before creating a new one
|
|
65
|
+
if (this.centrifugeClient) {
|
|
66
|
+
this.centrifugeClient.stop();
|
|
67
|
+
this.centrifugeClient = null;
|
|
68
|
+
}
|
|
48
69
|
const clientConfig = {
|
|
49
70
|
url: tokens.url,
|
|
50
71
|
connectionToken: tokens.connectionToken,
|
|
51
72
|
subscriptionToken: tokens.subscriptionToken,
|
|
52
|
-
channel
|
|
73
|
+
channel,
|
|
53
74
|
guid: this.config.guid ?? randomUUID(),
|
|
54
75
|
userId: this.config.userId,
|
|
55
76
|
httpBaseUrl: this.config.baseUrl ?? 'https://copilot.tencent.com',
|
|
@@ -59,18 +80,43 @@ export class WorkBuddyTransport {
|
|
|
59
80
|
const callbacks = {
|
|
60
81
|
onConnected: () => {
|
|
61
82
|
log.info('WorkBuddy Centrifuge connected');
|
|
83
|
+
log.info(`WorkBuddy sessionId (must match binding): ${workspaceSessionId}`);
|
|
84
|
+
this.reconnectAttempt = 0;
|
|
62
85
|
this.updateState('connected');
|
|
86
|
+
const doRegister = () => {
|
|
87
|
+
if (this.stopped || this.state !== 'connected')
|
|
88
|
+
return;
|
|
89
|
+
oauth.registerChannel({
|
|
90
|
+
type: 'wechatkf',
|
|
91
|
+
sessionId: workspaceSessionId,
|
|
92
|
+
channelId: channel,
|
|
93
|
+
userId: this.config.userId,
|
|
94
|
+
}).then((res) => {
|
|
95
|
+
log.info(`Channel registered (WeChat KF online): ${JSON.stringify(res)}`);
|
|
96
|
+
}).catch((err) => {
|
|
97
|
+
log.warn(`registerChannel heartbeat failed: ${String(err)}`);
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
// Register immediately and then keep heartbeat alive.
|
|
101
|
+
doRegister();
|
|
102
|
+
if (this.heartbeatTimer)
|
|
103
|
+
clearInterval(this.heartbeatTimer);
|
|
104
|
+
this.heartbeatTimer = setInterval(doRegister, CHANNEL_HEARTBEAT_MS);
|
|
63
105
|
},
|
|
64
106
|
onDisconnected: (reason) => {
|
|
65
107
|
log.info(`WorkBuddy Centrifuge disconnected: ${reason}`);
|
|
108
|
+
if (this.heartbeatTimer) {
|
|
109
|
+
clearInterval(this.heartbeatTimer);
|
|
110
|
+
this.heartbeatTimer = null;
|
|
111
|
+
}
|
|
66
112
|
this.updateState('disconnected');
|
|
113
|
+
this.scheduleReconnect();
|
|
67
114
|
},
|
|
68
115
|
onError: (error) => {
|
|
69
116
|
log.error('WorkBuddy Centrifuge error:', error);
|
|
70
117
|
this.updateState('error');
|
|
71
118
|
},
|
|
72
119
|
onMessage: async (chatId, msgId, content) => {
|
|
73
|
-
// Normalize WeChat KF or AGP message to AGPEnvelope
|
|
74
120
|
const envelope = {
|
|
75
121
|
msg_id: msgId,
|
|
76
122
|
guid: this.config.guid,
|
|
@@ -90,14 +136,37 @@ export class WorkBuddyTransport {
|
|
|
90
136
|
this.centrifugeClient = new WorkBuddyCentrifugeClient(clientConfig, callbacks);
|
|
91
137
|
this.centrifugeClient.start();
|
|
92
138
|
log.info('WorkBuddy transport initialized');
|
|
139
|
+
this.reconnectAttempt = 0;
|
|
93
140
|
}
|
|
94
141
|
catch (err) {
|
|
95
142
|
log.error('Failed to start WorkBuddy transport:', err);
|
|
96
143
|
this.updateState('error');
|
|
97
|
-
|
|
144
|
+
this.scheduleReconnect();
|
|
98
145
|
}
|
|
99
146
|
}
|
|
147
|
+
scheduleReconnect() {
|
|
148
|
+
if (this.stopped)
|
|
149
|
+
return;
|
|
150
|
+
if (this.reconnectTimer)
|
|
151
|
+
return;
|
|
152
|
+
const delayMs = RECONNECT_DELAYS_MS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS_MS.length - 1)];
|
|
153
|
+
this.reconnectAttempt++;
|
|
154
|
+
log.info(`WorkBuddy transport reconnecting in ${delayMs}ms (attempt ${this.reconnectAttempt})...`);
|
|
155
|
+
this.reconnectTimer = setTimeout(() => {
|
|
156
|
+
this.reconnectTimer = null;
|
|
157
|
+
void this.connect();
|
|
158
|
+
}, delayMs);
|
|
159
|
+
}
|
|
100
160
|
stop() {
|
|
161
|
+
this.stopped = true;
|
|
162
|
+
if (this.heartbeatTimer) {
|
|
163
|
+
clearInterval(this.heartbeatTimer);
|
|
164
|
+
this.heartbeatTimer = null;
|
|
165
|
+
}
|
|
166
|
+
if (this.reconnectTimer) {
|
|
167
|
+
clearTimeout(this.reconnectTimer);
|
|
168
|
+
this.reconnectTimer = null;
|
|
169
|
+
}
|
|
101
170
|
if (this.centrifugeClient) {
|
|
102
171
|
this.centrifugeClient.stop();
|
|
103
172
|
this.centrifugeClient = null;
|