@wu529778790/open-im 1.0.3-beta.0 → 1.0.3-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.
|
@@ -95,6 +95,29 @@ export function runClaude(cliPath, prompt, sessionId, workDir, callbacks, option
|
|
|
95
95
|
}
|
|
96
96
|
}, timeoutMs);
|
|
97
97
|
}
|
|
98
|
+
// stderr 截断:只保留首 4KB + 尾 6KB,减少 I/O 和内存
|
|
99
|
+
const MAX_STDERR_HEAD = 4 * 1024;
|
|
100
|
+
const MAX_STDERR_TAIL = 6 * 1024;
|
|
101
|
+
let stderrHead = "";
|
|
102
|
+
let stderrTail = "";
|
|
103
|
+
let stderrTotal = 0;
|
|
104
|
+
let stderrHeadFull = false;
|
|
105
|
+
child.stderr?.on("data", (chunk) => {
|
|
106
|
+
const text = chunk.toString();
|
|
107
|
+
stderrTotal += text.length;
|
|
108
|
+
if (!stderrHeadFull) {
|
|
109
|
+
const room = MAX_STDERR_HEAD - stderrHead.length;
|
|
110
|
+
if (room > 0) {
|
|
111
|
+
stderrHead += text.slice(0, room);
|
|
112
|
+
if (stderrHead.length >= MAX_STDERR_HEAD)
|
|
113
|
+
stderrHeadFull = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
stderrTail += text;
|
|
117
|
+
if (stderrTail.length > MAX_STDERR_TAIL) {
|
|
118
|
+
stderrTail = stderrTail.slice(-MAX_STDERR_TAIL);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
98
121
|
const rl = createInterface({ input: child.stdout });
|
|
99
122
|
rl.on("line", (line) => {
|
|
100
123
|
const event = parseStreamLine(line);
|
|
@@ -172,7 +195,22 @@ export function runClaude(cliPath, prompt, sessionId, workDir, callbacks, option
|
|
|
172
195
|
clearTimeout(timeoutHandle);
|
|
173
196
|
if (!completed) {
|
|
174
197
|
if (exitCode !== null && exitCode !== 0) {
|
|
175
|
-
|
|
198
|
+
let errMsg = "";
|
|
199
|
+
if (stderrTotal > 0) {
|
|
200
|
+
if (!stderrHeadFull) {
|
|
201
|
+
errMsg = stderrHead;
|
|
202
|
+
}
|
|
203
|
+
else if (stderrTotal <= MAX_STDERR_HEAD + MAX_STDERR_TAIL) {
|
|
204
|
+
errMsg = stderrHead + stderrTail.slice(stderrTail.length - (stderrTotal - MAX_STDERR_HEAD));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
errMsg =
|
|
208
|
+
stderrHead +
|
|
209
|
+
`\n\n... (省略 ${stderrTotal - MAX_STDERR_HEAD - MAX_STDERR_TAIL} 字节) ...\n\n` +
|
|
210
|
+
stderrTail;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
callbacks.onError(errMsg || `Claude CLI exited with code ${exitCode}`);
|
|
176
214
|
}
|
|
177
215
|
else {
|
|
178
216
|
callbacks.onComplete({
|
|
@@ -126,6 +126,29 @@ export class ClaudeProcessPool {
|
|
|
126
126
|
const toolStats = {};
|
|
127
127
|
const pendingToolInputs = new Map();
|
|
128
128
|
const startTime = Date.now();
|
|
129
|
+
// stderr 截断:只保留首 4KB + 尾 6KB
|
|
130
|
+
const MAX_STDERR_HEAD = 4 * 1024;
|
|
131
|
+
const MAX_STDERR_TAIL = 6 * 1024;
|
|
132
|
+
let stderrHead = "";
|
|
133
|
+
let stderrTail = "";
|
|
134
|
+
let stderrTotal = 0;
|
|
135
|
+
let stderrHeadFull = false;
|
|
136
|
+
child.stderr?.on("data", (chunk) => {
|
|
137
|
+
const text = chunk.toString();
|
|
138
|
+
stderrTotal += text.length;
|
|
139
|
+
if (!stderrHeadFull) {
|
|
140
|
+
const room = MAX_STDERR_HEAD - stderrHead.length;
|
|
141
|
+
if (room > 0) {
|
|
142
|
+
stderrHead += text.slice(0, room);
|
|
143
|
+
if (stderrHead.length >= MAX_STDERR_HEAD)
|
|
144
|
+
stderrHeadFull = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
stderrTail += text;
|
|
148
|
+
if (stderrTail.length > MAX_STDERR_TAIL) {
|
|
149
|
+
stderrTail = stderrTail.slice(-MAX_STDERR_TAIL);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
129
152
|
const rl = createInterface({ input: child.stdout });
|
|
130
153
|
rl.on("line", (line) => {
|
|
131
154
|
const event = parseStreamLine(line);
|
|
@@ -203,9 +226,26 @@ export class ClaudeProcessPool {
|
|
|
203
226
|
this.activeProcesses.delete(key);
|
|
204
227
|
resolved = true;
|
|
205
228
|
if (exitCode !== null && exitCode !== 0) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
229
|
+
let errorMsg = "";
|
|
230
|
+
if (stderrTotal > 0) {
|
|
231
|
+
if (!stderrHeadFull) {
|
|
232
|
+
errorMsg = stderrHead;
|
|
233
|
+
}
|
|
234
|
+
else if (stderrTotal <= MAX_STDERR_HEAD + MAX_STDERR_TAIL) {
|
|
235
|
+
errorMsg =
|
|
236
|
+
stderrHead +
|
|
237
|
+
stderrTail.slice(stderrTail.length - (stderrTotal - MAX_STDERR_HEAD));
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
errorMsg =
|
|
241
|
+
stderrHead +
|
|
242
|
+
`\n\n... (省略 ${stderrTotal - MAX_STDERR_HEAD - MAX_STDERR_TAIL} 字节) ...\n\n` +
|
|
243
|
+
stderrTail;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const msg = errorMsg || `Claude CLI exited with code ${exitCode}`;
|
|
247
|
+
callbacks.onError(msg);
|
|
248
|
+
reject(new Error(msg));
|
|
209
249
|
}
|
|
210
250
|
// If exitCode is 0 and we haven't resolved yet, the result was already sent
|
|
211
251
|
// via the extractResult handler. This is just cleanup.
|
package/dist/constants.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ export declare const IMAGE_DIR: string;
|
|
|
5
5
|
export declare const READ_ONLY_TOOLS: string[];
|
|
6
6
|
export declare const TERMINAL_ONLY_COMMANDS: Set<string>;
|
|
7
7
|
export declare const DEDUP_TTL_MS: number;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/** 飞书流式更新节流:≥200ms 以满足单条消息 5 QPS 频控,避免触发 delete 回退(撤回提示) */
|
|
9
|
+
export declare const FEISHU_THROTTLE_MS = 200;
|
|
10
|
+
/** Telegram 编辑消息节流:200ms(open-im 默认值) */
|
|
11
|
+
export declare const TELEGRAM_THROTTLE_MS = 200;
|
|
11
12
|
export declare const MAX_TELEGRAM_MESSAGE_LENGTH = 4000;
|
|
12
13
|
export declare const MAX_FEISHU_MESSAGE_LENGTH = 4000;
|
package/dist/constants.js
CHANGED
|
@@ -1,20 +1,42 @@
|
|
|
1
|
-
import { join } from
|
|
2
|
-
import { homedir, tmpdir } from
|
|
3
|
-
export const APP_HOME = join(homedir(),
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { homedir, tmpdir } from "node:os";
|
|
3
|
+
export const APP_HOME = join(homedir(), ".open-im");
|
|
4
4
|
/** 优雅关闭 HTTP 端口(stop 命令通过此端口触发 shutdown) */
|
|
5
5
|
export const SHUTDOWN_PORT = 39281;
|
|
6
|
-
export const IMAGE_DIR = join(tmpdir(),
|
|
6
|
+
export const IMAGE_DIR = join(tmpdir(), "open-im-images");
|
|
7
7
|
export const READ_ONLY_TOOLS = [
|
|
8
|
-
|
|
8
|
+
"Read",
|
|
9
|
+
"Glob",
|
|
10
|
+
"Grep",
|
|
11
|
+
"WebFetch",
|
|
12
|
+
"WebSearch",
|
|
13
|
+
"Task",
|
|
14
|
+
"TodoRead",
|
|
9
15
|
];
|
|
10
16
|
export const TERMINAL_ONLY_COMMANDS = new Set([
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
"/context",
|
|
18
|
+
"/rewind",
|
|
19
|
+
"/resume",
|
|
20
|
+
"/copy",
|
|
21
|
+
"/export",
|
|
22
|
+
"/config",
|
|
23
|
+
"/init",
|
|
24
|
+
"/memory",
|
|
25
|
+
"/permissions",
|
|
26
|
+
"/theme",
|
|
27
|
+
"/vim",
|
|
28
|
+
"/statusline",
|
|
29
|
+
"/terminal-setup",
|
|
30
|
+
"/debug",
|
|
31
|
+
"/tasks",
|
|
32
|
+
"/mcp",
|
|
33
|
+
"/teleport",
|
|
34
|
+
"/add-dir",
|
|
14
35
|
]);
|
|
15
36
|
export const DEDUP_TTL_MS = 5 * 60 * 1000;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
/** 飞书流式更新节流:≥200ms 以满足单条消息 5 QPS 频控,避免触发 delete 回退(撤回提示) */
|
|
38
|
+
export const FEISHU_THROTTLE_MS = 200;
|
|
39
|
+
/** Telegram 编辑消息节流:200ms(open-im 默认值) */
|
|
40
|
+
export const TELEGRAM_THROTTLE_MS = 200;
|
|
19
41
|
export const MAX_TELEGRAM_MESSAGE_LENGTH = 4000;
|
|
20
42
|
export const MAX_FEISHU_MESSAGE_LENGTH = 4000;
|
|
@@ -10,7 +10,7 @@ import { CommandHandler } from '../commands/handler.js';
|
|
|
10
10
|
import { getAdapter } from '../adapters/registry.js';
|
|
11
11
|
import { runAITask } from '../shared/ai-task.js';
|
|
12
12
|
import { startTaskCleanup } from '../shared/task-cleanup.js';
|
|
13
|
-
import {
|
|
13
|
+
import { FEISHU_THROTTLE_MS, IMAGE_DIR } from '../constants.js';
|
|
14
14
|
import { setActiveChatId } from '../shared/active-chats.js';
|
|
15
15
|
import { setChatUser } from '../shared/chat-user-map.js';
|
|
16
16
|
import { createLogger } from '../logger.js';
|
|
@@ -138,7 +138,7 @@ export function setupFeishuHandlers(config, sessionManager) {
|
|
|
138
138
|
const stopTyping = startTypingLoop(chatId);
|
|
139
139
|
const taskKey = `${userId}:${msgId}`;
|
|
140
140
|
await runAITask({ config, sessionManager }, { userId, chatId, workDir, sessionId, convId, platform: 'feishu', taskKey }, prompt, toolAdapter, {
|
|
141
|
-
throttleMs:
|
|
141
|
+
throttleMs: FEISHU_THROTTLE_MS,
|
|
142
142
|
streamUpdate: async (content, toolNote) => {
|
|
143
143
|
const note = toolNote ? '输出中...\n' + toolNote : '输出中...';
|
|
144
144
|
try {
|
|
@@ -311,7 +311,6 @@ export async function sendFinalMessages(chatId, messageId, fullContent, note, to
|
|
|
311
311
|
// If content fits in one message, try patch for smooth transition
|
|
312
312
|
if (parts.length === 1) {
|
|
313
313
|
const cardContent = createFeishuCard(getToolTitle(toolId, 'done'), fullContent, 'done');
|
|
314
|
-
// Try to use patch API for in-place update
|
|
315
314
|
try {
|
|
316
315
|
const resp = await client.im.message.patch({
|
|
317
316
|
path: { message_id: messageId },
|
|
@@ -341,7 +340,7 @@ export async function sendFinalMessages(chatId, messageId, fullContent, note, to
|
|
|
341
340
|
catch (err) {
|
|
342
341
|
log.warn('Failed to delete old message:', err);
|
|
343
342
|
}
|
|
344
|
-
// Send new messages
|
|
343
|
+
// Send new messages (split when content exceeds limit)
|
|
345
344
|
for (let i = 0; i < parts.length; i++) {
|
|
346
345
|
try {
|
|
347
346
|
const partContent = i === 0 ? parts[0] : `${parts[i]}\n\n_*(续 ${i + 1}/${parts.length})*_`;
|