@wu529778790/open-im 1.8.3-beta.10 → 1.8.3-beta.12
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 +23 -22
- package/dist/workbuddy/centrifuge-client.js +6 -6
- package/dist/workbuddy/client.js +60 -26
- package/dist/workbuddy/oauth.d.ts +1 -1
- package/dist/workbuddy/oauth.js +4 -4
- package/package.json +1 -1
package/dist/setup.js
CHANGED
|
@@ -461,9 +461,18 @@ export async function runInteractiveSetup() {
|
|
|
461
461
|
catch {
|
|
462
462
|
// account info is optional
|
|
463
463
|
}
|
|
464
|
-
const
|
|
464
|
+
const ai = accountInfo;
|
|
465
|
+
// Try multiple field names; fall back to tokenResult.userId, then existing config
|
|
466
|
+
const existingUserId = wb?.userId;
|
|
467
|
+
const userId = String(ai?.uid ?? ai?.userId ?? ai?.user_id ??
|
|
468
|
+
tokenResult.userId ??
|
|
469
|
+
existingUserId ?? "");
|
|
470
|
+
if (!userId) {
|
|
471
|
+
console.warn("⚠️ 未能获取 WorkBuddy 用户 ID,sessionId 可能不正确,建议稍后重试");
|
|
472
|
+
}
|
|
465
473
|
// Set oauth.userId so buildSessionId() produces the correct sessionId.
|
|
466
474
|
oauth.userId = userId;
|
|
475
|
+
console.log(` userId: ${userId || "(空)"}, sessionId 将为: ${oauth.buildSessionId()}`);
|
|
467
476
|
// Save credentials immediately — even if binding fails below
|
|
468
477
|
config.platforms.workbuddy = {
|
|
469
478
|
enabled: true,
|
|
@@ -473,22 +482,18 @@ export async function runInteractiveSetup() {
|
|
|
473
482
|
};
|
|
474
483
|
oauthOk = true;
|
|
475
484
|
console.log("\n✅ WorkBuddy 凭证已获取,正在生成微信客服绑定链接...");
|
|
476
|
-
// Phase 2: WeChat KF binding (non-fatal)
|
|
485
|
+
// Phase 2: WeChat KF binding link (non-fatal, no polling needed)
|
|
477
486
|
try {
|
|
478
|
-
const
|
|
487
|
+
const { join: pathJoin } = await import("node:path");
|
|
488
|
+
const { homedir } = await import("node:os");
|
|
489
|
+
const clawPath = pathJoin(homedir(), "WorkBuddy", "Claw");
|
|
490
|
+
const sessionId = oauth.buildSessionId(clawPath);
|
|
479
491
|
const linkResult = await oauth.getWeChatKfLink(sessionId);
|
|
480
492
|
if (linkResult.success && linkResult.url) {
|
|
481
493
|
console.log("\n━━━ 微信客服绑定 ━━━");
|
|
482
|
-
console.log("
|
|
494
|
+
console.log("请将以下链接发给微信「文件传输助手」并点击打开,即完成绑定:");
|
|
483
495
|
console.log(linkResult.url);
|
|
484
|
-
console.log("\n
|
|
485
|
-
const bindResult = await oauth.pollBindStatus(sessionId);
|
|
486
|
-
if (bindResult.bound) {
|
|
487
|
-
console.log(`✅ 微信客服绑定成功!${bindResult.nickname ? ` 用户: ${bindResult.nickname}` : ""}`);
|
|
488
|
-
}
|
|
489
|
-
else {
|
|
490
|
-
console.log("⚠️ 绑定超时,你可以稍后重新运行 open-im init 完成绑定");
|
|
491
|
-
}
|
|
496
|
+
console.log("\n绑定完成后运行 open-im start 启动服务即可收发消息。");
|
|
492
497
|
}
|
|
493
498
|
else {
|
|
494
499
|
console.log(`⚠️ 获取微信客服链接失败: ${linkResult.message ?? "未知错误"}`);
|
|
@@ -526,21 +531,17 @@ export async function runInteractiveSetup() {
|
|
|
526
531
|
userId: wb?.userId ?? '',
|
|
527
532
|
});
|
|
528
533
|
oauthKeep.userId = wb?.userId ?? '';
|
|
529
|
-
const
|
|
534
|
+
const { join: pathJoin2 } = await import("node:path");
|
|
535
|
+
const { homedir: homedir2 } = await import("node:os");
|
|
536
|
+
const clawPath2 = pathJoin2(homedir2(), "WorkBuddy", "Claw");
|
|
537
|
+
const sessionId = oauthKeep.buildSessionId(clawPath2);
|
|
530
538
|
console.log(`\n正在获取微信客服绑定链接... (sessionId: ${sessionId})`);
|
|
531
539
|
const linkResult = await oauthKeep.getWeChatKfLink(sessionId);
|
|
532
540
|
if (linkResult.success && linkResult.url) {
|
|
533
541
|
console.log("\n━━━ 微信客服绑定 ━━━");
|
|
534
|
-
console.log("
|
|
542
|
+
console.log("请将以下链接发给微信「文件传输助手」并点击打开,即完成绑定:");
|
|
535
543
|
console.log(linkResult.url);
|
|
536
|
-
console.log("\n
|
|
537
|
-
const bindResult = await oauthKeep.pollBindStatus(sessionId);
|
|
538
|
-
if (bindResult.bound) {
|
|
539
|
-
console.log(`✅ 微信客服绑定成功!${bindResult.nickname ? ` 用户: ${bindResult.nickname}` : ""}`);
|
|
540
|
-
}
|
|
541
|
-
else {
|
|
542
|
-
console.log("⚠️ 绑定超时,你可以稍后重新运行 open-im init 完成绑定");
|
|
543
|
-
}
|
|
544
|
+
console.log("\n绑定完成后运行 open-im start 启动服务即可收发消息。");
|
|
544
545
|
}
|
|
545
546
|
else {
|
|
546
547
|
console.log(`⚠️ 获取绑定链接失败: ${linkResult.message ?? "未知"}`);
|
|
@@ -145,20 +145,20 @@ export class WorkBuddyCentrifugeClient {
|
|
|
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
|
-
// chatId format: "<
|
|
149
|
-
//
|
|
148
|
+
// Incoming chatId format: "<wechatExternalUserId>::origin::wechatkfProxy"
|
|
149
|
+
// Server requires chatId = just the WeChat external user ID (the part before "::").
|
|
150
150
|
const chatIdFull = payload.session_id;
|
|
151
|
-
const
|
|
151
|
+
const chatId = chatIdFull.includes('::') ? chatIdFull.split('::')[0] : chatIdFull;
|
|
152
152
|
const httpPayload = {
|
|
153
153
|
type: 'COPILOT_RESPONSE',
|
|
154
154
|
msgId: payload.prompt_id,
|
|
155
|
-
chatId
|
|
156
|
-
toUser,
|
|
155
|
+
chatId, // WeChat external user ID only — server uses this as touser
|
|
157
156
|
success: payload.stop_reason === 'end_turn',
|
|
158
157
|
message,
|
|
159
158
|
metadata: {
|
|
160
159
|
sessionId: this.config.workspaceSessionId || payload.session_id,
|
|
161
|
-
|
|
160
|
+
requestId: payload.prompt_id,
|
|
161
|
+
state: payload.stop_reason === 'end_turn' ? 'completed' : payload.stop_reason,
|
|
162
162
|
},
|
|
163
163
|
};
|
|
164
164
|
const url = `${this.config.httpBaseUrl}/v2/backgroundagent/wecom/local-proxy/receive`;
|
package/dist/workbuddy/client.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { randomUUID } from 'node:crypto';
|
|
8
8
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
|
-
import { hostname } from 'node:os';
|
|
10
|
+
import { hostname, homedir } from 'node:os';
|
|
11
11
|
import { createLogger } from '../logger.js';
|
|
12
12
|
import { WorkBuddyOAuth } from './oauth.js';
|
|
13
13
|
import { WorkBuddyCentrifugeClient } from './centrifuge-client.js';
|
|
@@ -62,27 +62,30 @@ async function connect() {
|
|
|
62
62
|
const pc = platformConfig;
|
|
63
63
|
const baseUrl = pc.baseUrl ?? 'https://copilot.tencent.com';
|
|
64
64
|
const hostId = hostname();
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
// Claw workspace path — matches the plugin's WorkBuddy Claw installation path.
|
|
66
|
+
// The directory does NOT need to exist; the server uses it as a string identifier.
|
|
67
|
+
const clawPath = join(homedir(), 'WorkBuddy', 'Claw');
|
|
68
|
+
log.info('Registering WorkBuddy host workspace...');
|
|
67
69
|
let tokens;
|
|
68
70
|
try {
|
|
71
|
+
// Step 1: Register host workspace (workspaceId="") — gets the Centrifuge connection
|
|
69
72
|
tokens = await oauth.registerWorkspace({
|
|
70
73
|
userId: pc.userId ?? '',
|
|
71
74
|
hostId,
|
|
72
|
-
workspaceId:
|
|
73
|
-
workspaceName: '
|
|
75
|
+
workspaceId: '',
|
|
76
|
+
workspaceName: 'Host Channel',
|
|
74
77
|
});
|
|
75
78
|
}
|
|
76
79
|
catch (err) {
|
|
77
|
-
log.error('
|
|
80
|
+
log.error('Host workspace registration failed:', err);
|
|
78
81
|
scheduleReconnect();
|
|
79
82
|
return;
|
|
80
83
|
}
|
|
81
|
-
// sessionId
|
|
82
|
-
const workspaceSessionId = oauth.buildSessionId();
|
|
84
|
+
// sessionId = userId_hostId_clawPath (matches plugin's buildSessionId format)
|
|
85
|
+
const workspaceSessionId = oauth.buildSessionId(clawPath);
|
|
83
86
|
const channel = tokens.channel;
|
|
84
87
|
const guid = pc.guid ?? randomUUID();
|
|
85
|
-
log.info(`
|
|
88
|
+
log.info(`Host workspace registered: channel=${channel}, clawSessionId=${workspaceSessionId}`);
|
|
86
89
|
if (centrifugeClient) {
|
|
87
90
|
centrifugeClient.stop();
|
|
88
91
|
centrifugeClient = null;
|
|
@@ -100,25 +103,56 @@ async function connect() {
|
|
|
100
103
|
}, {
|
|
101
104
|
onConnected: () => {
|
|
102
105
|
log.info('WorkBuddy Centrifuge connected');
|
|
103
|
-
log.info(`
|
|
106
|
+
log.info(`WeChat KF sessionId: ${workspaceSessionId}`);
|
|
104
107
|
reconnectAttempt = 0;
|
|
105
108
|
updateState('connected');
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
109
|
+
// Step 2: Register Claw workspace to get WeChat KF routing channel + sessionId
|
|
110
|
+
oauth.registerWorkspace({
|
|
111
|
+
userId: pc.userId ?? '',
|
|
112
|
+
hostId,
|
|
113
|
+
workspaceId: clawPath,
|
|
114
|
+
workspaceName: 'Claw',
|
|
115
|
+
}).then((clawParams) => {
|
|
116
|
+
const clawSessionId = clawParams.sessionId ?? workspaceSessionId;
|
|
117
|
+
log.info(`Claw workspace registered: channel=${clawParams.channel}, sessionId=${clawSessionId}`);
|
|
118
|
+
// Subscribe to Claw channel — WeChat KF messages are published here
|
|
119
|
+
centrifugeClient?.subscribeChannel(clawParams.channel, clawParams.subscriptionToken);
|
|
120
|
+
const doRegister = () => {
|
|
121
|
+
if (stopped || channelState !== 'connected')
|
|
122
|
+
return;
|
|
123
|
+
oauth.registerChannel({
|
|
124
|
+
type: 'wechatkf',
|
|
125
|
+
sessionId: clawSessionId,
|
|
126
|
+
channelId: pc.userId ?? '', // plugin uses userId, not full channel name
|
|
127
|
+
userId: pc.userId ?? '',
|
|
128
|
+
})
|
|
129
|
+
.then((res) => log.info(`WeChat KF channel registered (online): ${JSON.stringify(res)}`))
|
|
130
|
+
.catch((err) => log.warn(`registerChannel failed: ${String(err)}`));
|
|
131
|
+
};
|
|
132
|
+
doRegister();
|
|
133
|
+
if (heartbeatTimer)
|
|
134
|
+
clearInterval(heartbeatTimer);
|
|
135
|
+
heartbeatTimer = setInterval(doRegister, CHANNEL_HEARTBEAT_MS);
|
|
136
|
+
}).catch((err) => {
|
|
137
|
+
log.error('Claw workspace registration failed:', err);
|
|
138
|
+
// Fallback: register with host sessionId
|
|
139
|
+
const doRegister = () => {
|
|
140
|
+
if (stopped || channelState !== 'connected')
|
|
141
|
+
return;
|
|
142
|
+
oauth.registerChannel({
|
|
143
|
+
type: 'wechatkf',
|
|
144
|
+
sessionId: workspaceSessionId,
|
|
145
|
+
channelId: pc.userId ?? '',
|
|
146
|
+
userId: pc.userId ?? '',
|
|
147
|
+
})
|
|
148
|
+
.then((res) => log.info(`WeChat KF channel registered (fallback): ${JSON.stringify(res)}`))
|
|
149
|
+
.catch((e) => log.warn(`registerChannel failed: ${String(e)}`));
|
|
150
|
+
};
|
|
151
|
+
doRegister();
|
|
152
|
+
if (heartbeatTimer)
|
|
153
|
+
clearInterval(heartbeatTimer);
|
|
154
|
+
heartbeatTimer = setInterval(doRegister, CHANNEL_HEARTBEAT_MS);
|
|
155
|
+
});
|
|
122
156
|
},
|
|
123
157
|
onDisconnected: (reason) => {
|
|
124
158
|
log.info(`WorkBuddy Centrifuge disconnected: ${reason}`);
|
package/dist/workbuddy/oauth.js
CHANGED
|
@@ -174,10 +174,10 @@ export class WorkBuddyOAuth {
|
|
|
174
174
|
/**
|
|
175
175
|
* Build sessionId for WorkBuddy workspace
|
|
176
176
|
*/
|
|
177
|
-
buildSessionId(
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
return `${this.userId}_${this.hostId}`;
|
|
177
|
+
buildSessionId(workspacePath) {
|
|
178
|
+
// Must match the plugin's format: ${userId}_${hostId}_${workspacePath}
|
|
179
|
+
// The server uses this as the chatId/routing key for WeChat KF messages.
|
|
180
|
+
return `${this.userId}_${this.hostId}_${workspacePath ?? ''}`;
|
|
181
181
|
}
|
|
182
182
|
/**
|
|
183
183
|
* Get WeChat KF binding link
|