@wu529778790/open-im 1.2.3 → 1.2.4-beta.1
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/README.md +0 -1
- package/dist/cli.js +3 -45
- package/dist/feishu/message-sender.js +41 -25
- package/dist/index.js +0 -5
- package/dist/session/session-manager.d.ts +0 -4
- package/dist/session/session-manager.js +0 -15
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -88,14 +88,9 @@ async function validateOrSetup() {
|
|
|
88
88
|
// ============================================================================
|
|
89
89
|
// 命令处理
|
|
90
90
|
// ============================================================================
|
|
91
|
-
async function cmdStart(
|
|
91
|
+
async function cmdStart() {
|
|
92
92
|
const pid = getPid();
|
|
93
|
-
|
|
94
|
-
if (skipPlatformPrompt && pid) {
|
|
95
|
-
// 强制清理,即使进程看起来在运行
|
|
96
|
-
removePid();
|
|
97
|
-
}
|
|
98
|
-
else if (pid && isRunning(pid)) {
|
|
93
|
+
if (pid && isRunning(pid)) {
|
|
99
94
|
console.log(`open-im 已在后台运行 (pid=${pid})`);
|
|
100
95
|
return;
|
|
101
96
|
}
|
|
@@ -106,9 +101,8 @@ async function cmdStart(skipPlatformPrompt = false) {
|
|
|
106
101
|
process.exit(1);
|
|
107
102
|
}
|
|
108
103
|
// 有 TTY 时在父进程让用户选择要启用的平台,再启动子进程
|
|
109
|
-
// skipPlatformPrompt 为 true 时跳过提示(用于 restart 命令)
|
|
110
104
|
let config = loadConfig();
|
|
111
|
-
if (process.stdin.isTTY
|
|
105
|
+
if (process.stdin.isTTY) {
|
|
112
106
|
const updated = await runPlatformSelectionPrompt(config);
|
|
113
107
|
if (!updated) {
|
|
114
108
|
console.log("已取消启动。");
|
|
@@ -170,40 +164,6 @@ async function cmdStop() {
|
|
|
170
164
|
}
|
|
171
165
|
console.log(`open-im 已停止 (pid=${pid})`);
|
|
172
166
|
}
|
|
173
|
-
async function cmdRestart() {
|
|
174
|
-
const pid = getPid();
|
|
175
|
-
const wasRunning = pid && isRunning(pid);
|
|
176
|
-
if (wasRunning) {
|
|
177
|
-
console.log(`正在重启 open-im (当前 pid=${pid})...`);
|
|
178
|
-
console.log(' → 正在停止服务...');
|
|
179
|
-
await cmdStop();
|
|
180
|
-
// 等待进程完全停止
|
|
181
|
-
for (let i = 0; i < 50; i++) {
|
|
182
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
183
|
-
if (!isRunning(pid)) {
|
|
184
|
-
console.log(' → 服务已停止');
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// 额外等待一下,确保所有资源(端口、文件句柄等)完全释放
|
|
189
|
-
console.log(' → 等待资源释放...');
|
|
190
|
-
await new Promise((r) => setTimeout(r, 1000));
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
if (pid) {
|
|
194
|
-
console.log(`检测到 PID 文件存在 (pid=${pid}),但进程未运行,清理后重启...`);
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
console.log('open-im 未在后台运行,直接启动...');
|
|
198
|
-
}
|
|
199
|
-
removePid();
|
|
200
|
-
}
|
|
201
|
-
console.log(' → 正在启动服务...');
|
|
202
|
-
// 设置环境变量,告诉 main() 函数这是 restart,需要清除旧的 sessionId
|
|
203
|
-
process.env.OPEN_IM_RESTART = '1';
|
|
204
|
-
await cmdStart(true); // 传递 true 跳过平台选择提示
|
|
205
|
-
delete process.env.OPEN_IM_RESTART;
|
|
206
|
-
}
|
|
207
167
|
async function cmdInit() {
|
|
208
168
|
console.log("\n━━━ open-im 配置向导 ━━━\n");
|
|
209
169
|
const saved = await runInteractiveSetup();
|
|
@@ -225,7 +185,6 @@ function showHelp(exitCode = 0) {
|
|
|
225
185
|
命令:
|
|
226
186
|
start 后台运行服务
|
|
227
187
|
stop 停止后台服务
|
|
228
|
-
restart 重启服务
|
|
229
188
|
init 配置向导(首次或追加配置,会覆盖已有 config.json)
|
|
230
189
|
dev 前台运行(调试模式),Ctrl+C 停止
|
|
231
190
|
|
|
@@ -241,7 +200,6 @@ const cmd = process.argv[2];
|
|
|
241
200
|
const commands = {
|
|
242
201
|
start: cmdStart,
|
|
243
202
|
stop: cmdStop,
|
|
244
|
-
restart: cmdRestart,
|
|
245
203
|
init: cmdInit,
|
|
246
204
|
dev: main,
|
|
247
205
|
};
|
|
@@ -253,37 +253,54 @@ export async function sendThinkingMessage(chatId, replyToMessageId, toolId = 'cl
|
|
|
253
253
|
throw err;
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
|
+
// Track if patch API is working for this session
|
|
257
|
+
let patchApiWorking = true;
|
|
258
|
+
let patchFailCount = 0;
|
|
259
|
+
const MAX_PATCH_FAILURES_BEFORE_DISABLE = 3;
|
|
256
260
|
export async function updateMessage(chatId, messageId, content, status, note, toolId = 'claude') {
|
|
257
261
|
const client = getClient();
|
|
258
262
|
const title = getToolTitle(toolId, status);
|
|
259
263
|
const cardContent = createFeishuCard(title, content, status, note);
|
|
260
264
|
// Try to use patch API for in-place update (streaming)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
265
|
+
// Only attempt patch if it has been working recently
|
|
266
|
+
if (patchApiWorking) {
|
|
267
|
+
try {
|
|
268
|
+
const resp = await client.im.message.patch({
|
|
269
|
+
path: { message_id: messageId },
|
|
270
|
+
data: {
|
|
271
|
+
content: cardContent,
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
if (resp.code === 0) {
|
|
275
|
+
log.debug(`✓ Patch API succeeded: ${messageId}`);
|
|
276
|
+
patchFailCount = 0; // Reset failure count on success
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
// Patch failed with API error
|
|
280
|
+
patchFailCount++;
|
|
281
|
+
log.warn(`Patch API failed (code: ${resp.code}, msg: ${resp.msg}) - failure ${patchFailCount}/${MAX_PATCH_FAILURES_BEFORE_DISABLE}`);
|
|
282
|
+
if (patchFailCount >= MAX_PATCH_FAILURES_BEFORE_DISABLE) {
|
|
283
|
+
log.warn('Patch API disabled for this session due to repeated failures');
|
|
284
|
+
patchApiWorking = false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
// Patch failed with network/other error
|
|
289
|
+
patchFailCount++;
|
|
290
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
291
|
+
log.warn(`Patch API error (${patchFailCount}/${MAX_PATCH_FAILURES_BEFORE_DISABLE}): ${errorMsg}`);
|
|
292
|
+
if (patchFailCount >= MAX_PATCH_FAILURES_BEFORE_DISABLE) {
|
|
293
|
+
log.warn('Patch API disabled for this session due to repeated errors');
|
|
294
|
+
patchApiWorking = false;
|
|
295
|
+
}
|
|
271
296
|
}
|
|
272
|
-
// If patch failed with validation error, fall back to delete+create
|
|
273
|
-
log.warn(`Patch API failed (code: ${resp.code}, msg: ${resp.msg}), falling back to delete+create`);
|
|
274
|
-
}
|
|
275
|
-
catch (err) {
|
|
276
|
-
// Log but don't throw - we'll fall back to delete+create
|
|
277
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
278
|
-
log.debug(`Patch API error: ${errorMsg}, falling back to delete+create`);
|
|
279
297
|
}
|
|
280
298
|
// Fallback: Delete old message and send new one
|
|
281
299
|
try {
|
|
282
|
-
log.info(`Deleting old message ${messageId}`);
|
|
283
300
|
await client.im.message.delete({
|
|
284
301
|
path: { message_id: messageId },
|
|
285
302
|
});
|
|
286
|
-
log.
|
|
303
|
+
log.debug(`Deleted old message for recreate: ${messageId}`);
|
|
287
304
|
}
|
|
288
305
|
catch (err) {
|
|
289
306
|
log.warn('Failed to delete old message:', err);
|
|
@@ -298,7 +315,7 @@ export async function updateMessage(chatId, messageId, content, status, note, to
|
|
|
298
315
|
},
|
|
299
316
|
params: { receive_id_type: 'chat_id' },
|
|
300
317
|
});
|
|
301
|
-
log.
|
|
318
|
+
log.debug(`Created new message: ${resp.data?.message_id}`);
|
|
302
319
|
}
|
|
303
320
|
catch (err) {
|
|
304
321
|
log.error('Failed to send new message:', err);
|
|
@@ -308,8 +325,8 @@ export async function updateMessage(chatId, messageId, content, status, note, to
|
|
|
308
325
|
export async function sendFinalMessages(chatId, messageId, fullContent, note, toolId = 'claude') {
|
|
309
326
|
const client = getClient();
|
|
310
327
|
const parts = splitLongContent(fullContent, MAX_FEISHU_MESSAGE_LENGTH);
|
|
311
|
-
// If content fits in one message, try patch for smooth transition
|
|
312
|
-
if (parts.length === 1) {
|
|
328
|
+
// If content fits in one message and patch is working, try patch for smooth transition
|
|
329
|
+
if (parts.length === 1 && patchApiWorking) {
|
|
313
330
|
const cardContent = createFeishuCard(getToolTitle(toolId, 'done'), fullContent, 'done');
|
|
314
331
|
try {
|
|
315
332
|
const resp = await client.im.message.patch({
|
|
@@ -319,7 +336,7 @@ export async function sendFinalMessages(chatId, messageId, fullContent, note, to
|
|
|
319
336
|
},
|
|
320
337
|
});
|
|
321
338
|
if (resp.code === 0) {
|
|
322
|
-
log.info(
|
|
339
|
+
log.info(`✓ Final message patched successfully: ${messageId}`);
|
|
323
340
|
return;
|
|
324
341
|
}
|
|
325
342
|
log.warn(`Patch API failed (code: ${resp.code}), falling back to delete+create`);
|
|
@@ -331,11 +348,10 @@ export async function sendFinalMessages(chatId, messageId, fullContent, note, to
|
|
|
331
348
|
}
|
|
332
349
|
// Fallback: Delete old message first (for multi-part or failed patch)
|
|
333
350
|
try {
|
|
334
|
-
log.info(`Deleting old message ${messageId}`);
|
|
335
351
|
await client.im.message.delete({
|
|
336
352
|
path: { message_id: messageId },
|
|
337
353
|
});
|
|
338
|
-
log.
|
|
354
|
+
log.debug(`Deleted old message for final recreate: ${messageId}`);
|
|
339
355
|
}
|
|
340
356
|
catch (err) {
|
|
341
357
|
log.warn('Failed to delete old message:', err);
|
package/dist/index.js
CHANGED
|
@@ -103,11 +103,6 @@ export async function main() {
|
|
|
103
103
|
log.info(`默认权限模式: ${defaultModeLabel} (${config.defaultPermissionMode})`);
|
|
104
104
|
log.info(`启用平台: ${config.enabledPlatforms.join(", ")}`);
|
|
105
105
|
const sessionManager = new SessionManager(config.claudeWorkDir, config.allowedBaseDirs);
|
|
106
|
-
// 如果是 restart,清除所有旧的 sessionId(SDK 内部状态已失效)
|
|
107
|
-
if (process.env.OPEN_IM_RESTART === '1') {
|
|
108
|
-
sessionManager.clearAllSessionIds();
|
|
109
|
-
delete process.env.OPEN_IM_RESTART;
|
|
110
|
-
}
|
|
111
106
|
let telegramHandle = null;
|
|
112
107
|
let feishuHandle = null;
|
|
113
108
|
let wechatHandle = null;
|
|
@@ -19,10 +19,6 @@ export declare class SessionManager {
|
|
|
19
19
|
setModel(userId: string, model: string | undefined, threadId?: string): void;
|
|
20
20
|
private resolveAndValidate;
|
|
21
21
|
private load;
|
|
22
|
-
/**
|
|
23
|
-
* 清除所有会话的 sessionId(用于 restart 后 SDK 内部状态失效的情况)
|
|
24
|
-
*/
|
|
25
|
-
clearAllSessionIds(): void;
|
|
26
22
|
private save;
|
|
27
23
|
private flushSync;
|
|
28
24
|
destroy(): void;
|
|
@@ -213,21 +213,6 @@ export class SessionManager {
|
|
|
213
213
|
/* ignore */
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
|
-
/**
|
|
217
|
-
* 清除所有会话的 sessionId(用于 restart 后 SDK 内部状态失效的情况)
|
|
218
|
-
*/
|
|
219
|
-
clearAllSessionIds() {
|
|
220
|
-
for (const session of this.sessions.values()) {
|
|
221
|
-
session.sessionId = undefined;
|
|
222
|
-
if (session.threads) {
|
|
223
|
-
for (const t of Object.values(session.threads)) {
|
|
224
|
-
t.sessionId = undefined;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
this.flushSync();
|
|
229
|
-
log.info('All sessionIds cleared for restart');
|
|
230
|
-
}
|
|
231
216
|
save() {
|
|
232
217
|
if (this.saveTimer)
|
|
233
218
|
return;
|