@wu529778790/open-im 1.9.3-beta.13 → 1.9.3-beta.14
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/adapters/claude-sdk-adapter.js +34 -0
- package/dist/adapters/codebuddy-adapter.js +1 -0
- package/dist/adapters/codex-adapter.js +1 -0
- package/dist/adapters/tool-adapter.interface.d.ts +1 -0
- package/dist/codebuddy/cli-runner.d.ts +1 -0
- package/dist/codebuddy/cli-runner.js +61 -0
- package/dist/codex/cli-runner.d.ts +1 -0
- package/dist/codex/cli-runner.js +64 -0
- package/dist/config-web-page-i18n.d.ts +6 -0
- package/dist/config-web-page-i18n.js +6 -0
- package/dist/config-web-page-script.js +9 -0
- package/dist/config-web-page-template.js +12 -0
- package/dist/config-web.js +15 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +16 -2
- package/dist/dingtalk/event-handler.js +3 -1
- package/dist/feishu/event-handler.js +3 -1
- package/dist/qq/event-handler.js +3 -1
- package/dist/setup.js +5 -2
- package/dist/shared/ai-task.js +6 -0
- package/dist/shared/ai-task.test.js +3 -0
- package/dist/shared/task-cleanup.d.ts +2 -0
- package/dist/shared/task-cleanup.js +19 -0
- package/dist/telegram/event-handler.js +3 -1
- package/dist/wework/event-handler.js +3 -1
- package/dist/workbuddy/event-handler.js +3 -1
- package/package.json +1 -1
|
@@ -212,6 +212,7 @@ export class ClaudeSDKAdapter {
|
|
|
212
212
|
let pendingTempId; // 记录临时 ID,用于 abort 时清理
|
|
213
213
|
let runSettled = false;
|
|
214
214
|
let currentStream; // 用于 abort 时立即中断 stream
|
|
215
|
+
let timeoutHandle;
|
|
215
216
|
const permissionMode = options?.skipPermissions
|
|
216
217
|
? 'bypassPermissions'
|
|
217
218
|
: options?.permissionMode === 'acceptEdits'
|
|
@@ -304,6 +305,8 @@ export class ClaudeSDKAdapter {
|
|
|
304
305
|
log.info(`[V2] Result: subtype=${m.subtype}, num_turns=${m.num_turns}, sessionId=${actualSessionId ?? 'unknown'}`);
|
|
305
306
|
// 检查会话错误
|
|
306
307
|
if (!success) {
|
|
308
|
+
if (timeoutHandle)
|
|
309
|
+
clearTimeout(timeoutHandle);
|
|
307
310
|
runSettled = true;
|
|
308
311
|
const noConvErr = errs.find((e) => e.includes('No conversation found') || e.includes('session not found'));
|
|
309
312
|
if (noConvErr) {
|
|
@@ -340,6 +343,8 @@ export class ClaudeSDKAdapter {
|
|
|
340
343
|
result.result = accumulated;
|
|
341
344
|
}
|
|
342
345
|
runSettled = true;
|
|
346
|
+
if (timeoutHandle)
|
|
347
|
+
clearTimeout(timeoutHandle);
|
|
343
348
|
callbacks.onComplete(result);
|
|
344
349
|
return;
|
|
345
350
|
}
|
|
@@ -348,6 +353,8 @@ export class ClaudeSDKAdapter {
|
|
|
348
353
|
if (!streamClosed) {
|
|
349
354
|
if (accumulated) {
|
|
350
355
|
log.info('Stream ended without result message, using accumulated text');
|
|
356
|
+
if (timeoutHandle)
|
|
357
|
+
clearTimeout(timeoutHandle);
|
|
351
358
|
runSettled = true;
|
|
352
359
|
callbacks.onComplete({
|
|
353
360
|
success: true,
|
|
@@ -362,6 +369,8 @@ export class ClaudeSDKAdapter {
|
|
|
362
369
|
else {
|
|
363
370
|
// 流结束但无 result 也无 accumulated:必须触发回调,否则 Promise 永远挂起
|
|
364
371
|
log.warn('Stream ended with no result and no accumulated text, calling onError to prevent stuck state');
|
|
372
|
+
if (timeoutHandle)
|
|
373
|
+
clearTimeout(timeoutHandle);
|
|
365
374
|
runSettled = true;
|
|
366
375
|
callbacks.onError('AI 响应异常结束(无输出),请重试');
|
|
367
376
|
}
|
|
@@ -384,6 +393,8 @@ export class ClaudeSDKAdapter {
|
|
|
384
393
|
return;
|
|
385
394
|
}
|
|
386
395
|
runSettled = true;
|
|
396
|
+
if (timeoutHandle)
|
|
397
|
+
clearTimeout(timeoutHandle);
|
|
387
398
|
const errorObj = err;
|
|
388
399
|
const msg = errorObj.message || String(err);
|
|
389
400
|
log.error(`Claude SDK V2 error: ${msg}`);
|
|
@@ -417,15 +428,38 @@ export class ClaudeSDKAdapter {
|
|
|
417
428
|
runSession().catch((err) => {
|
|
418
429
|
if (!runSettled) {
|
|
419
430
|
runSettled = true;
|
|
431
|
+
if (timeoutHandle)
|
|
432
|
+
clearTimeout(timeoutHandle);
|
|
420
433
|
const msg = err instanceof Error ? err.message : String(err);
|
|
421
434
|
log.error(`Unhandled runSession error: ${msg}`);
|
|
422
435
|
callbacks.onError(msg);
|
|
423
436
|
}
|
|
424
437
|
});
|
|
438
|
+
// 强制执行超时
|
|
439
|
+
if (options?.timeoutMs && options.timeoutMs > 0) {
|
|
440
|
+
timeoutHandle = setTimeout(() => {
|
|
441
|
+
if (!runSettled) {
|
|
442
|
+
log.warn(`Session timed out after ${options.timeoutMs}ms, aborting`);
|
|
443
|
+
abortController.abort();
|
|
444
|
+
// 立即中断 stream,不等下一条消息
|
|
445
|
+
if (currentStream) {
|
|
446
|
+
try {
|
|
447
|
+
currentStream.return?.();
|
|
448
|
+
}
|
|
449
|
+
catch { /* ignore */ }
|
|
450
|
+
}
|
|
451
|
+
runSettled = true;
|
|
452
|
+
callbacks.onError(`AI 响应超时(${Math.round(options.timeoutMs / 1000)}s),请重试`);
|
|
453
|
+
}
|
|
454
|
+
}, options.timeoutMs);
|
|
455
|
+
timeoutHandle.unref();
|
|
456
|
+
}
|
|
425
457
|
return {
|
|
426
458
|
abort: () => {
|
|
427
459
|
log.info('Aborting session run');
|
|
428
460
|
abortController.abort();
|
|
461
|
+
if (timeoutHandle)
|
|
462
|
+
clearTimeout(timeoutHandle);
|
|
429
463
|
// 立即中断 stream,不等下一条消息
|
|
430
464
|
if (currentStream) {
|
|
431
465
|
try {
|
|
@@ -25,6 +25,7 @@ export interface RunOptions {
|
|
|
25
25
|
skipPermissions?: boolean;
|
|
26
26
|
/** Claude --permission-mode: default | acceptEdits | plan(yolo 时用 skipPermissions) */
|
|
27
27
|
permissionMode?: 'default' | 'acceptEdits' | 'plan';
|
|
28
|
+
timeoutMs?: number;
|
|
28
29
|
model?: string;
|
|
29
30
|
chatId?: string;
|
|
30
31
|
hookPort?: number;
|
|
@@ -3,6 +3,18 @@ import { accessSync, constants } from 'node:fs';
|
|
|
3
3
|
import { isAbsolute, join } from 'node:path';
|
|
4
4
|
import { createLogger } from '../logger.js';
|
|
5
5
|
const log = createLogger('CodeBuddyCli');
|
|
6
|
+
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
7
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
8
|
+
function getIdleTimeoutMs(totalTimeoutMs) {
|
|
9
|
+
const raw = process.env.CODEBUDDY_IDLE_TIMEOUT_MS;
|
|
10
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
11
|
+
const configuredIdleTimeoutMs = Number.isFinite(parsed) && parsed > 0
|
|
12
|
+
? Math.min(parsed, MAX_TIMEOUT_MS)
|
|
13
|
+
: DEFAULT_IDLE_TIMEOUT_MS;
|
|
14
|
+
return totalTimeoutMs > 0
|
|
15
|
+
? Math.min(configuredIdleTimeoutMs, totalTimeoutMs)
|
|
16
|
+
: configuredIdleTimeoutMs;
|
|
17
|
+
}
|
|
6
18
|
export function buildCodeBuddyArgs(prompt, sessionId, options) {
|
|
7
19
|
const args = ['--print', '--output-format', 'stream-json'];
|
|
8
20
|
if (options?.skipPermissions) {
|
|
@@ -191,7 +203,50 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
191
203
|
let currentModel;
|
|
192
204
|
const toolStats = {};
|
|
193
205
|
const startTime = Date.now();
|
|
206
|
+
const timeoutMs = options?.timeoutMs && options.timeoutMs > 0
|
|
207
|
+
? Math.min(options.timeoutMs, MAX_TIMEOUT_MS)
|
|
208
|
+
: 0;
|
|
209
|
+
const idleTimeoutMs = getIdleTimeoutMs(timeoutMs);
|
|
210
|
+
let timeoutHandle = null;
|
|
211
|
+
let idleTimeoutHandle = null;
|
|
194
212
|
const stdoutState = { buffer: '' };
|
|
213
|
+
const clearTimers = () => {
|
|
214
|
+
if (timeoutHandle) {
|
|
215
|
+
clearTimeout(timeoutHandle);
|
|
216
|
+
timeoutHandle = null;
|
|
217
|
+
}
|
|
218
|
+
if (idleTimeoutHandle) {
|
|
219
|
+
clearTimeout(idleTimeoutHandle);
|
|
220
|
+
idleTimeoutHandle = null;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const resetIdleTimeout = () => {
|
|
224
|
+
if (idleTimeoutMs <= 0 || completed)
|
|
225
|
+
return;
|
|
226
|
+
if (idleTimeoutHandle)
|
|
227
|
+
clearTimeout(idleTimeoutHandle);
|
|
228
|
+
idleTimeoutHandle = setTimeout(() => {
|
|
229
|
+
if (completed)
|
|
230
|
+
return;
|
|
231
|
+
completed = true;
|
|
232
|
+
clearTimers();
|
|
233
|
+
if (!child.killed)
|
|
234
|
+
child.kill('SIGTERM');
|
|
235
|
+
callbacks.onError(`CodeBuddy 执行长时间无输出,已自动终止(${idleTimeoutMs}ms)`);
|
|
236
|
+
}, idleTimeoutMs);
|
|
237
|
+
};
|
|
238
|
+
if (timeoutMs > 0) {
|
|
239
|
+
timeoutHandle = setTimeout(() => {
|
|
240
|
+
if (completed)
|
|
241
|
+
return;
|
|
242
|
+
completed = true;
|
|
243
|
+
clearTimers();
|
|
244
|
+
if (!child.killed)
|
|
245
|
+
child.kill('SIGTERM');
|
|
246
|
+
callbacks.onError(`CodeBuddy 执行超时(${timeoutMs}ms),已终止`);
|
|
247
|
+
}, timeoutMs);
|
|
248
|
+
}
|
|
249
|
+
resetIdleTimeout();
|
|
195
250
|
const MAX_STDERR = 8 * 1024;
|
|
196
251
|
let stderrText = '';
|
|
197
252
|
const handleErrorText = (message) => {
|
|
@@ -244,6 +299,7 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
244
299
|
if (completed)
|
|
245
300
|
return;
|
|
246
301
|
completed = true;
|
|
302
|
+
clearTimers();
|
|
247
303
|
const isError = payload.is_error === true;
|
|
248
304
|
const resultText = typeof payload.result === 'string'
|
|
249
305
|
? payload.result
|
|
@@ -268,6 +324,7 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
268
324
|
}
|
|
269
325
|
};
|
|
270
326
|
child.stdout?.on('data', (chunk) => {
|
|
327
|
+
resetIdleTimeout();
|
|
271
328
|
stdoutState.buffer += chunk.toString();
|
|
272
329
|
const payloads = extractBufferedPayloads(stdoutState);
|
|
273
330
|
for (const payload of payloads) {
|
|
@@ -280,12 +337,14 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
280
337
|
}
|
|
281
338
|
});
|
|
282
339
|
child.stderr?.on('data', (chunk) => {
|
|
340
|
+
resetIdleTimeout();
|
|
283
341
|
stderrText += chunk.toString();
|
|
284
342
|
if (stderrText.length > MAX_STDERR) {
|
|
285
343
|
stderrText = stderrText.slice(-MAX_STDERR);
|
|
286
344
|
}
|
|
287
345
|
});
|
|
288
346
|
child.on('close', (code) => {
|
|
347
|
+
clearTimers();
|
|
289
348
|
if (completed)
|
|
290
349
|
return;
|
|
291
350
|
if (stdoutState.buffer.trim()) {
|
|
@@ -318,6 +377,7 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
318
377
|
});
|
|
319
378
|
});
|
|
320
379
|
child.on('error', (err) => {
|
|
380
|
+
clearTimers();
|
|
321
381
|
if (completed)
|
|
322
382
|
return;
|
|
323
383
|
completed = true;
|
|
@@ -326,6 +386,7 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
326
386
|
return {
|
|
327
387
|
abort: () => {
|
|
328
388
|
completed = true;
|
|
389
|
+
clearTimers();
|
|
329
390
|
if (!child.killed)
|
|
330
391
|
child.kill('SIGTERM');
|
|
331
392
|
},
|
package/dist/codex/cli-runner.js
CHANGED
|
@@ -8,6 +8,8 @@ import { createInterface } from 'node:readline';
|
|
|
8
8
|
import { createLogger } from '../logger.js';
|
|
9
9
|
const log = createLogger('CodexCli');
|
|
10
10
|
const windowsCodexLaunchCache = new Map();
|
|
11
|
+
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
12
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
11
13
|
const SUPPORTED_IMAGE_EXTENSIONS = new Set([
|
|
12
14
|
'.png',
|
|
13
15
|
'.jpg',
|
|
@@ -19,6 +21,16 @@ const SUPPORTED_IMAGE_EXTENSIONS = new Set([
|
|
|
19
21
|
'.tiff',
|
|
20
22
|
'.avif',
|
|
21
23
|
]);
|
|
24
|
+
function getIdleTimeoutMs(totalTimeoutMs) {
|
|
25
|
+
const raw = process.env.CODEX_IDLE_TIMEOUT_MS;
|
|
26
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
27
|
+
const configuredIdleTimeoutMs = Number.isFinite(parsed) && parsed > 0
|
|
28
|
+
? Math.min(parsed, MAX_TIMEOUT_MS)
|
|
29
|
+
: DEFAULT_IDLE_TIMEOUT_MS;
|
|
30
|
+
return totalTimeoutMs > 0
|
|
31
|
+
? Math.min(configuredIdleTimeoutMs, totalTimeoutMs)
|
|
32
|
+
: configuredIdleTimeoutMs;
|
|
33
|
+
}
|
|
22
34
|
function parseCodexEvent(line) {
|
|
23
35
|
const trimmed = line.trim();
|
|
24
36
|
if (!trimmed)
|
|
@@ -213,7 +225,51 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
213
225
|
let completed = false;
|
|
214
226
|
const toolStats = {};
|
|
215
227
|
const startTime = Date.now();
|
|
228
|
+
const timeoutMs = options?.timeoutMs && options.timeoutMs > 0
|
|
229
|
+
? Math.min(options.timeoutMs, MAX_TIMEOUT_MS)
|
|
230
|
+
: 0;
|
|
231
|
+
const idleTimeoutMs = getIdleTimeoutMs(timeoutMs);
|
|
232
|
+
let timeoutHandle = null;
|
|
233
|
+
let idleTimeoutHandle = null;
|
|
216
234
|
const rl = createInterface({ input: child.stdout });
|
|
235
|
+
const clearTimers = () => {
|
|
236
|
+
if (timeoutHandle) {
|
|
237
|
+
clearTimeout(timeoutHandle);
|
|
238
|
+
timeoutHandle = null;
|
|
239
|
+
}
|
|
240
|
+
if (idleTimeoutHandle) {
|
|
241
|
+
clearTimeout(idleTimeoutHandle);
|
|
242
|
+
idleTimeoutHandle = null;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const failAndTerminate = (message, logMessage) => {
|
|
246
|
+
if (completed)
|
|
247
|
+
return;
|
|
248
|
+
completed = true;
|
|
249
|
+
clearTimers();
|
|
250
|
+
log.warn(logMessage);
|
|
251
|
+
rl.close();
|
|
252
|
+
if (!child.killed)
|
|
253
|
+
child.kill('SIGTERM');
|
|
254
|
+
callbacks.onError(message);
|
|
255
|
+
};
|
|
256
|
+
const resetIdleTimeout = () => {
|
|
257
|
+
if (idleTimeoutMs <= 0 || completed)
|
|
258
|
+
return;
|
|
259
|
+
if (idleTimeoutHandle)
|
|
260
|
+
clearTimeout(idleTimeoutHandle);
|
|
261
|
+
idleTimeoutHandle = setTimeout(() => {
|
|
262
|
+
failAndTerminate(`Codex 执行长时间无输出,已自动终止(${idleTimeoutMs}ms)`, `Codex CLI idle timeout after ${idleTimeoutMs}ms, killing pid=${child.pid}`);
|
|
263
|
+
}, idleTimeoutMs);
|
|
264
|
+
};
|
|
265
|
+
if (timeoutMs > 0) {
|
|
266
|
+
timeoutHandle = setTimeout(() => {
|
|
267
|
+
if (!completed && !child.killed) {
|
|
268
|
+
failAndTerminate(`执行超时(${timeoutMs}ms),已终止进程`, `Codex CLI timeout after ${timeoutMs}ms, killing pid=${child.pid}`);
|
|
269
|
+
}
|
|
270
|
+
}, timeoutMs);
|
|
271
|
+
}
|
|
272
|
+
resetIdleTimeout();
|
|
217
273
|
const MAX_STDERR_HEAD = 4 * 1024;
|
|
218
274
|
const MAX_STDERR_TAIL = 6 * 1024;
|
|
219
275
|
let stderrHead = '';
|
|
@@ -221,6 +277,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
221
277
|
let stderrTotal = 0;
|
|
222
278
|
let stderrHeadFull = false;
|
|
223
279
|
child.stderr?.on('data', (chunk) => {
|
|
280
|
+
resetIdleTimeout();
|
|
224
281
|
const text = chunk.toString();
|
|
225
282
|
stderrTotal += text.length;
|
|
226
283
|
if (!stderrHeadFull) {
|
|
@@ -238,6 +295,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
238
295
|
log.debug(`[stderr] ${text.trimEnd()}`);
|
|
239
296
|
});
|
|
240
297
|
rl.on('line', (line) => {
|
|
298
|
+
resetIdleTimeout();
|
|
241
299
|
const event = parseCodexEvent(line);
|
|
242
300
|
if (!event)
|
|
243
301
|
return;
|
|
@@ -251,6 +309,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
251
309
|
}
|
|
252
310
|
if (type === 'turn.failed') {
|
|
253
311
|
completed = true;
|
|
312
|
+
clearTimers();
|
|
254
313
|
const err = event.error;
|
|
255
314
|
callbacks.onError(err?.message ?? 'Codex turn failed');
|
|
256
315
|
return;
|
|
@@ -261,6 +320,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
261
320
|
return;
|
|
262
321
|
}
|
|
263
322
|
completed = true;
|
|
323
|
+
clearTimers();
|
|
264
324
|
callbacks.onError(msg ?? 'Codex stream error');
|
|
265
325
|
return;
|
|
266
326
|
}
|
|
@@ -314,6 +374,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
314
374
|
}
|
|
315
375
|
if (type === 'turn.completed') {
|
|
316
376
|
completed = true;
|
|
377
|
+
clearTimers();
|
|
317
378
|
callbacks.onComplete({
|
|
318
379
|
success: true,
|
|
319
380
|
result: accumulated,
|
|
@@ -331,6 +392,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
331
392
|
const finalize = () => {
|
|
332
393
|
if (!rlClosed || !childClosed)
|
|
333
394
|
return;
|
|
395
|
+
clearTimers();
|
|
334
396
|
if (completed)
|
|
335
397
|
return;
|
|
336
398
|
if (exitCode !== null && exitCode !== 0) {
|
|
@@ -381,6 +443,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
381
443
|
child.on('error', (err) => {
|
|
382
444
|
const errorCode = err.code;
|
|
383
445
|
log.error(`Codex CLI spawn error: ${err.message}, code=${errorCode}, path=${cliPath}`);
|
|
446
|
+
clearTimers();
|
|
384
447
|
if (!completed) {
|
|
385
448
|
completed = true;
|
|
386
449
|
callbacks.onError(`Failed to start Codex CLI: ${err.message}`);
|
|
@@ -391,6 +454,7 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
391
454
|
return {
|
|
392
455
|
abort: () => {
|
|
393
456
|
completed = true;
|
|
457
|
+
clearTimers();
|
|
394
458
|
rl.close();
|
|
395
459
|
if (!child.killed)
|
|
396
460
|
child.kill('SIGTERM');
|
|
@@ -101,11 +101,14 @@ export declare const PAGE_TEXTS: {
|
|
|
101
101
|
readonly codexCli: "Codex CLI path";
|
|
102
102
|
readonly codebuddyCli: "CodeBuddy CLI path";
|
|
103
103
|
readonly codexProxy: "Codex proxy";
|
|
104
|
+
readonly claudeTimeout: "Claude timeout (ms)";
|
|
104
105
|
readonly claudeConfigPath: "Config file location";
|
|
105
106
|
readonly claudeAuthToken: "ANTHROPIC_AUTH_TOKEN";
|
|
106
107
|
readonly claudeBaseUrl: "ANTHROPIC_BASE_URL";
|
|
107
108
|
readonly claudeModel: "ANTHROPIC_MODEL";
|
|
108
109
|
readonly claudeProxy: "Proxy (optional)";
|
|
110
|
+
readonly codexTimeout: "Codex timeout (ms)";
|
|
111
|
+
readonly codebuddyTimeout: "CodeBuddy timeout (ms)";
|
|
109
112
|
readonly hookPort: "Hook port";
|
|
110
113
|
readonly logLevel: "Log level";
|
|
111
114
|
readonly logLevelDefault: "default (app default)";
|
|
@@ -250,8 +253,11 @@ export declare const PAGE_TEXTS: {
|
|
|
250
253
|
readonly codexCli: "Codex CLI 路径";
|
|
251
254
|
readonly codebuddyCli: "CodeBuddy CLI 路径";
|
|
252
255
|
readonly codexProxy: "Codex 代理";
|
|
256
|
+
readonly claudeTimeout: "Claude 超时(毫秒)";
|
|
253
257
|
readonly claudeConfigPath: "配置文件位置";
|
|
254
258
|
readonly claudeProxy: "代理(可选)";
|
|
259
|
+
readonly codexTimeout: "Codex 超时(毫秒)";
|
|
260
|
+
readonly codebuddyTimeout: "CodeBuddy 超时(毫秒)";
|
|
255
261
|
readonly hookPort: "Hook 端口";
|
|
256
262
|
readonly logLevel: "日志级别";
|
|
257
263
|
readonly logLevelDefault: "default(程序默认)";
|
|
@@ -101,11 +101,14 @@ export const PAGE_TEXTS = {
|
|
|
101
101
|
codexCli: "Codex CLI path",
|
|
102
102
|
codebuddyCli: "CodeBuddy CLI path",
|
|
103
103
|
codexProxy: "Codex proxy",
|
|
104
|
+
claudeTimeout: "Claude timeout (ms)",
|
|
104
105
|
claudeConfigPath: "Config file location",
|
|
105
106
|
claudeAuthToken: "ANTHROPIC_AUTH_TOKEN",
|
|
106
107
|
claudeBaseUrl: "ANTHROPIC_BASE_URL",
|
|
107
108
|
claudeModel: "ANTHROPIC_MODEL",
|
|
108
109
|
claudeProxy: "Proxy (optional)",
|
|
110
|
+
codexTimeout: "Codex timeout (ms)",
|
|
111
|
+
codebuddyTimeout: "CodeBuddy timeout (ms)",
|
|
109
112
|
hookPort: "Hook port",
|
|
110
113
|
logLevel: "Log level",
|
|
111
114
|
logLevelDefault: "default (app default)",
|
|
@@ -250,8 +253,11 @@ export const PAGE_TEXTS = {
|
|
|
250
253
|
codexCli: "Codex CLI \u8def\u5f84",
|
|
251
254
|
codebuddyCli: "CodeBuddy CLI \u8def\u5f84",
|
|
252
255
|
codexProxy: "Codex \u4ee3\u7406",
|
|
256
|
+
claudeTimeout: "Claude \u8d85\u65f6\uff08\u6beb\u79d2\uff09",
|
|
253
257
|
claudeConfigPath: "\u914d\u7f6e\u6587\u4ef6\u4f4d\u7f6e",
|
|
254
258
|
claudeProxy: "\u4ee3\u7406\uff08\u53ef\u9009\uff09",
|
|
259
|
+
codexTimeout: "Codex \u8d85\u65f6\uff08\u6beb\u79d2\uff09",
|
|
260
|
+
codebuddyTimeout: "CodeBuddy \u8d85\u65f6\uff08\u6beb\u79d2\uff09",
|
|
255
261
|
hookPort: "Hook \u7aef\u53e3",
|
|
256
262
|
logLevel: "\u65e5\u5fd7\u7ea7\u522b",
|
|
257
263
|
logLevelDefault: "default\uff08\u7a0b\u5e8f\u9ed8\u8ba4\uff09",
|
|
@@ -229,11 +229,14 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
229
229
|
{ id: "aiCommonTitle", key: "aiCommonTitle" },
|
|
230
230
|
{ id: "ai-aiCommand-label", key: "aiTool" },
|
|
231
231
|
{ id: "ai-claudeWorkDir-label", key: "workDir" },
|
|
232
|
+
{ id: "ai-claudeTimeoutMs-label", key: "claudeTimeout" },
|
|
232
233
|
{ id: "ai-claudeConfigPath-label", key: "claudeConfigPath" },
|
|
233
234
|
{ id: "ai-claudeProxy-label", key: "claudeProxy" },
|
|
234
235
|
{ id: "ai-codexCliPath-label", key: "codexCli" },
|
|
236
|
+
{ id: "ai-codexTimeoutMs-label", key: "codexTimeout" },
|
|
235
237
|
{ id: "ai-codexProxy-label", key: "codexProxy" },
|
|
236
238
|
{ id: "ai-codebuddyCliPath-label", key: "codebuddyCli" },
|
|
239
|
+
{ id: "ai-codebuddyTimeoutMs-label", key: "codebuddyTimeout" },
|
|
237
240
|
{ id: "ai-hookPort-label", key: "hookPort" },
|
|
238
241
|
{ id: "ai-logLevel-label", key: "logLevel" },
|
|
239
242
|
],
|
|
@@ -596,11 +599,14 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
596
599
|
const AI_FIELD_MAPPINGS = [
|
|
597
600
|
{ id: "ai-aiCommand", key: "aiCommand" },
|
|
598
601
|
{ id: "ai-claudeWorkDir", key: "claudeWorkDir" },
|
|
602
|
+
{ id: "ai-claudeTimeoutMs", key: "claudeTimeoutMs" },
|
|
599
603
|
{ id: "ai-claudeConfigPath", key: "claudeConfigPath" },
|
|
600
604
|
{ id: "ai-claudeProxy", key: "claudeProxy" },
|
|
601
605
|
{ id: "ai-codexCliPath", key: "codexCliPath" },
|
|
606
|
+
{ id: "ai-codexTimeoutMs", key: "codexTimeoutMs" },
|
|
602
607
|
{ id: "ai-codexProxy", key: "codexProxy" },
|
|
603
608
|
{ id: "ai-codebuddyCliPath", key: "codebuddyCliPath" },
|
|
609
|
+
{ id: "ai-codebuddyTimeoutMs", key: "codebuddyTimeoutMs" },
|
|
604
610
|
{ id: "ai-hookPort", key: "hookPort" },
|
|
605
611
|
{ id: "ai-logLevel", key: "logLevel" },
|
|
606
612
|
];
|
|
@@ -917,8 +923,11 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
917
923
|
ai: {
|
|
918
924
|
aiCommand: getValue("ai-aiCommand"),
|
|
919
925
|
claudeWorkDir: getValue("ai-claudeWorkDir"),
|
|
926
|
+
claudeTimeoutMs: getNumber("ai-claudeTimeoutMs"),
|
|
920
927
|
claudeConfigPath: getValue("ai-claudeConfigPath"),
|
|
921
928
|
claudeProxy: getValue("ai-claudeProxy"),
|
|
929
|
+
codexTimeoutMs: getNumber("ai-codexTimeoutMs"),
|
|
930
|
+
codebuddyTimeoutMs: getNumber("ai-codebuddyTimeoutMs"),
|
|
922
931
|
codexCliPath: getValue("ai-codexCliPath"),
|
|
923
932
|
codexProxy: getValue("ai-codexProxy"),
|
|
924
933
|
codebuddyCliPath: getValue("ai-codebuddyCliPath"),
|
|
@@ -1237,6 +1237,10 @@ export const PAGE_HTML_PREFIX = String.raw `<!doctype html>
|
|
|
1237
1237
|
</div>
|
|
1238
1238
|
<div class="ai-card-body">
|
|
1239
1239
|
<div id="ai-tool-claude" class="ai-tool-panel active" data-tool-panel="claude">
|
|
1240
|
+
<div class="form-group">
|
|
1241
|
+
<label class="form-label" id="ai-claudeTimeoutMs-label">Timeout (ms)</label>
|
|
1242
|
+
<input id="ai-claudeTimeoutMs" class="form-input" type="number" min="1" />
|
|
1243
|
+
</div>
|
|
1240
1244
|
<div class="form-group">
|
|
1241
1245
|
<label class="form-label" id="ai-claudeProxy-label">Proxy (optional)</label>
|
|
1242
1246
|
<input id="ai-claudeProxy" class="form-input mono" type="text" />
|
|
@@ -1255,6 +1259,10 @@ export const PAGE_HTML_PREFIX = String.raw `<!doctype html>
|
|
|
1255
1259
|
<label class="form-label" id="ai-codexCliPath-label">CLI Path</label>
|
|
1256
1260
|
<input id="ai-codexCliPath" class="form-input mono" type="text" />
|
|
1257
1261
|
</div>
|
|
1262
|
+
<div class="form-group">
|
|
1263
|
+
<label class="form-label" id="ai-codexTimeoutMs-label">Timeout (ms)</label>
|
|
1264
|
+
<input id="ai-codexTimeoutMs" class="form-input" type="number" min="1" />
|
|
1265
|
+
</div>
|
|
1258
1266
|
<div class="form-group">
|
|
1259
1267
|
<label class="form-label" id="ai-codexProxy-label">Proxy (optional)</label>
|
|
1260
1268
|
<input id="ai-codexProxy" class="form-input mono" type="text" />
|
|
@@ -1266,6 +1274,10 @@ export const PAGE_HTML_PREFIX = String.raw `<!doctype html>
|
|
|
1266
1274
|
<label class="form-label" id="ai-codebuddyCliPath-label">CLI Path</label>
|
|
1267
1275
|
<input id="ai-codebuddyCliPath" class="form-input mono" type="text" />
|
|
1268
1276
|
</div>
|
|
1277
|
+
<div class="form-group">
|
|
1278
|
+
<label class="form-label" id="ai-codebuddyTimeoutMs-label">Timeout (ms)</label>
|
|
1279
|
+
<input id="ai-codebuddyTimeoutMs" class="form-input" type="number" min="1" />
|
|
1280
|
+
</div>
|
|
1269
1281
|
</div>
|
|
1270
1282
|
</div>
|
|
1271
1283
|
</div>
|
package/dist/config-web.js
CHANGED
|
@@ -278,6 +278,7 @@ function buildInitialPayload(file) {
|
|
|
278
278
|
ai: {
|
|
279
279
|
aiCommand: file.aiCommand ?? "claude",
|
|
280
280
|
claudeWorkDir: file.tools?.claude?.workDir ?? process.cwd(),
|
|
281
|
+
claudeTimeoutMs: file.tools?.claude?.timeoutMs ?? 600000,
|
|
281
282
|
claudeConfigPath: process.platform === 'win32'
|
|
282
283
|
? getClaudeConfigHome() + "\\.claude\\settings.json"
|
|
283
284
|
: getClaudeConfigHome() + "/.claude/settings.json",
|
|
@@ -285,6 +286,8 @@ function buildInitialPayload(file) {
|
|
|
285
286
|
claudeBaseUrl: claudeEnv.ANTHROPIC_BASE_URL ?? "",
|
|
286
287
|
claudeModel: claudeEnv.ANTHROPIC_MODEL ?? "",
|
|
287
288
|
claudeProxy: file.tools?.claude?.proxy ?? "",
|
|
289
|
+
codexTimeoutMs: file.tools?.codex?.timeoutMs ?? 600000,
|
|
290
|
+
codebuddyTimeoutMs: file.tools?.codebuddy?.timeoutMs ?? 600000,
|
|
288
291
|
codexCliPath: file.tools?.codex?.cliPath ?? "codex",
|
|
289
292
|
codebuddyCliPath: file.tools?.codebuddy?.cliPath ?? "codebuddy",
|
|
290
293
|
codexProxy: file.tools?.codex?.proxy ?? "",
|
|
@@ -324,6 +327,12 @@ function validatePayload(payload) {
|
|
|
324
327
|
errors.push("WorkBuddy user ID is required.");
|
|
325
328
|
if (!clean(payload.ai.claudeWorkDir))
|
|
326
329
|
errors.push("Default work directory is required.");
|
|
330
|
+
if (!Number.isFinite(payload.ai.claudeTimeoutMs) || payload.ai.claudeTimeoutMs <= 0)
|
|
331
|
+
errors.push("Claude timeout must be positive.");
|
|
332
|
+
if (!Number.isFinite(payload.ai.codexTimeoutMs) || payload.ai.codexTimeoutMs <= 0)
|
|
333
|
+
errors.push("Codex timeout must be positive.");
|
|
334
|
+
if (!Number.isFinite(payload.ai.codebuddyTimeoutMs) || payload.ai.codebuddyTimeoutMs <= 0)
|
|
335
|
+
errors.push("CodeBuddy timeout must be positive.");
|
|
327
336
|
return errors;
|
|
328
337
|
}
|
|
329
338
|
function validateConfigForPlatform(platform, config) {
|
|
@@ -413,6 +422,9 @@ function createProbeConfig(values) {
|
|
|
413
422
|
aiCommand: "claude",
|
|
414
423
|
codexCliPath: "codex",
|
|
415
424
|
claudeWorkDir: process.cwd(),
|
|
425
|
+
claudeTimeoutMs: 600000,
|
|
426
|
+
codexTimeoutMs: 600000,
|
|
427
|
+
codebuddyTimeoutMs: 600000,
|
|
416
428
|
logDir: "",
|
|
417
429
|
logLevel: "INFO",
|
|
418
430
|
codebuddyCliPath: "codebuddy",
|
|
@@ -576,6 +588,7 @@ function toFileConfig(payload, existing) {
|
|
|
576
588
|
claude: {
|
|
577
589
|
...existing.tools?.claude,
|
|
578
590
|
workDir: clean(payload.ai.claudeWorkDir) ?? process.cwd(),
|
|
591
|
+
timeoutMs: payload.ai.claudeTimeoutMs,
|
|
579
592
|
proxy: clean(payload.ai.claudeProxy),
|
|
580
593
|
// model is now saved to ~/.claude/settings.json as env var
|
|
581
594
|
},
|
|
@@ -583,11 +596,13 @@ function toFileConfig(payload, existing) {
|
|
|
583
596
|
...existing.tools?.codex,
|
|
584
597
|
cliPath: clean(payload.ai.codexCliPath) ?? "codex",
|
|
585
598
|
workDir: clean(payload.ai.claudeWorkDir) ?? process.cwd(),
|
|
599
|
+
timeoutMs: payload.ai.codexTimeoutMs,
|
|
586
600
|
proxy: clean(payload.ai.codexProxy),
|
|
587
601
|
},
|
|
588
602
|
codebuddy: {
|
|
589
603
|
...existing.tools?.codebuddy,
|
|
590
604
|
cliPath: clean(payload.ai.codebuddyCliPath) ?? "codebuddy",
|
|
605
|
+
timeoutMs: payload.ai.codebuddyTimeoutMs,
|
|
591
606
|
},
|
|
592
607
|
},
|
|
593
608
|
platforms: {
|
package/dist/config.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export interface Config {
|
|
|
28
28
|
claudeProxy?: string;
|
|
29
29
|
/** Codex 访问 chatgpt.com 的代理(如 http://127.0.0.1:7890) */
|
|
30
30
|
codexProxy?: string;
|
|
31
|
+
claudeTimeoutMs: number;
|
|
32
|
+
codexTimeoutMs: number;
|
|
33
|
+
codebuddyTimeoutMs: number;
|
|
31
34
|
claudeWorkDir: string;
|
|
32
35
|
claudeModel?: string;
|
|
33
36
|
logDir: string;
|
|
@@ -134,6 +137,7 @@ interface FilePlatformWorkBuddy {
|
|
|
134
137
|
export interface FileToolClaude {
|
|
135
138
|
cliPath?: string;
|
|
136
139
|
workDir?: string;
|
|
140
|
+
timeoutMs?: number;
|
|
137
141
|
skipPermissions?: boolean;
|
|
138
142
|
/** HTTP/HTTPS 代理,用于访问 Claude API(如 http://127.0.0.1:7890) */
|
|
139
143
|
proxy?: string;
|
|
@@ -143,11 +147,13 @@ export interface FileToolClaude {
|
|
|
143
147
|
export interface FileToolCodex {
|
|
144
148
|
cliPath?: string;
|
|
145
149
|
workDir?: string;
|
|
150
|
+
timeoutMs?: number;
|
|
146
151
|
/** HTTP/HTTPS 代理,用于访问 chatgpt.com(如 http://127.0.0.1:7890) */
|
|
147
152
|
proxy?: string;
|
|
148
153
|
}
|
|
149
154
|
export interface FileToolCodeBuddy {
|
|
150
155
|
cliPath?: string;
|
|
156
|
+
timeoutMs?: number;
|
|
151
157
|
}
|
|
152
158
|
export interface FileConfig {
|
|
153
159
|
telegramBotToken?: string;
|
package/dist/config.js
CHANGED
|
@@ -20,8 +20,7 @@ const CODEX_AUTH_PATHS = [
|
|
|
20
20
|
];
|
|
21
21
|
const OLD_ROOT_KEYS = [
|
|
22
22
|
'claudeWorkDir',
|
|
23
|
-
'claudeTimeoutMs',
|
|
24
|
-
'claudeModel',
|
|
23
|
+
'claudeTimeoutMs', 'claudeModel',
|
|
25
24
|
];
|
|
26
25
|
// Config cache with mtime tracking
|
|
27
26
|
let cachedConfig = null;
|
|
@@ -58,6 +57,7 @@ function migrateToNewConfigFormat(raw) {
|
|
|
58
57
|
claude: {
|
|
59
58
|
...tc,
|
|
60
59
|
workDir: tc.workDir ?? raw.claudeWorkDir ?? process.cwd(),
|
|
60
|
+
timeoutMs: tc.timeoutMs ?? raw.claudeTimeoutMs ?? 600000,
|
|
61
61
|
proxy: tc.proxy,
|
|
62
62
|
// model 现在通过 env 配置,不再在这里处理
|
|
63
63
|
},
|
|
@@ -65,11 +65,13 @@ function migrateToNewConfigFormat(raw) {
|
|
|
65
65
|
...tcod,
|
|
66
66
|
cliPath: tcod.cliPath ?? 'codex',
|
|
67
67
|
workDir: tcod.workDir ?? raw.claudeWorkDir ?? process.cwd(),
|
|
68
|
+
timeoutMs: tcod.timeoutMs ?? raw.claudeTimeoutMs ?? 600000,
|
|
68
69
|
proxy: tcod.proxy,
|
|
69
70
|
},
|
|
70
71
|
codebuddy: {
|
|
71
72
|
...tcb,
|
|
72
73
|
cliPath: tcb.cliPath ?? 'codebuddy',
|
|
74
|
+
timeoutMs: tcb.timeoutMs ?? raw.claudeTimeoutMs ?? 600000,
|
|
73
75
|
},
|
|
74
76
|
};
|
|
75
77
|
for (const k of OLD_ROOT_KEYS) {
|
|
@@ -405,6 +407,15 @@ export function loadConfig() {
|
|
|
405
407
|
}
|
|
406
408
|
}
|
|
407
409
|
const claudeWorkDir = process.env.CLAUDE_WORK_DIR ?? tc.workDir ?? process.cwd();
|
|
410
|
+
const claudeTimeoutMs = process.env.CLAUDE_TIMEOUT_MS !== undefined
|
|
411
|
+
? parseInt(process.env.CLAUDE_TIMEOUT_MS, 10) || 600000
|
|
412
|
+
: tc.timeoutMs ?? 600000;
|
|
413
|
+
const codexTimeoutMs = process.env.CODEX_TIMEOUT_MS !== undefined
|
|
414
|
+
? parseInt(process.env.CODEX_TIMEOUT_MS, 10) || 600000
|
|
415
|
+
: tcod.timeoutMs ?? 600000;
|
|
416
|
+
const codebuddyTimeoutMs = process.env.CODEBUDDY_TIMEOUT_MS !== undefined
|
|
417
|
+
? parseInt(process.env.CODEBUDDY_TIMEOUT_MS, 10) || 600000
|
|
418
|
+
: tcb.timeoutMs ?? 600000;
|
|
408
419
|
// 6. 校验 Claude API 凭证(SDK 模式需要)
|
|
409
420
|
// 支持:官方 API Key、Auth Token、或自定义 API(第三方模型等,BASE_URL + token)
|
|
410
421
|
if (aiCommand === 'claude') {
|
|
@@ -628,6 +639,9 @@ export function loadConfig() {
|
|
|
628
639
|
claudeProxy,
|
|
629
640
|
codexProxy,
|
|
630
641
|
claudeWorkDir,
|
|
642
|
+
claudeTimeoutMs,
|
|
643
|
+
codexTimeoutMs,
|
|
644
|
+
codebuddyTimeoutMs,
|
|
631
645
|
claudeModel: process.env.ANTHROPIC_MODEL,
|
|
632
646
|
logDir,
|
|
633
647
|
logLevel,
|
|
@@ -6,6 +6,7 @@ import { ackMessage, downloadRobotMessageFile, registerSessionWebhook } from './
|
|
|
6
6
|
import { CommandHandler } from '../commands/handler.js';
|
|
7
7
|
import { getAdapter } from '../adapters/registry.js';
|
|
8
8
|
import { runAITask } from '../shared/ai-task.js';
|
|
9
|
+
import { startTaskCleanup } from '../shared/task-cleanup.js';
|
|
9
10
|
import { setActiveChatId, setDingTalkActiveTarget } from '../shared/active-chats.js';
|
|
10
11
|
import { setChatUser } from '../shared/chat-user-map.js';
|
|
11
12
|
import { createLogger } from '../logger.js';
|
|
@@ -158,6 +159,7 @@ export function setupDingTalkHandlers(config, sessionManager) {
|
|
|
158
159
|
const accessControl = new AccessControl(config.dingtalkAllowedUserIds);
|
|
159
160
|
const requestQueue = new RequestQueue();
|
|
160
161
|
const runningTasks = new Map();
|
|
162
|
+
const stopTaskCleanup = startTaskCleanup(runningTasks);
|
|
161
163
|
const commandHandler = new CommandHandler({
|
|
162
164
|
config,
|
|
163
165
|
sessionManager,
|
|
@@ -299,7 +301,7 @@ export function setupDingTalkHandlers(config, sessionManager) {
|
|
|
299
301
|
ackMessage(callbackId, { queued: enqueueResult });
|
|
300
302
|
}
|
|
301
303
|
return {
|
|
302
|
-
stop: () =>
|
|
304
|
+
stop: () => stopTaskCleanup(),
|
|
303
305
|
getRunningTaskCount: () => runningTasks.size,
|
|
304
306
|
handleEvent,
|
|
305
307
|
};
|
|
@@ -7,6 +7,7 @@ import { getAdapter } from '../adapters/registry.js';
|
|
|
7
7
|
import { runAITask } from '../shared/ai-task.js';
|
|
8
8
|
import { buildCardV2 } from './card-builder.js';
|
|
9
9
|
import { disableStreaming, updateCardFull, destroySession } from './cardkit-manager.js';
|
|
10
|
+
import { startTaskCleanup } from '../shared/task-cleanup.js';
|
|
10
11
|
import { CARDKIT_THROTTLE_MS } from '../constants.js';
|
|
11
12
|
import { setActiveChatId } from '../shared/active-chats.js';
|
|
12
13
|
import { setChatUser } from '../shared/chat-user-map.js';
|
|
@@ -125,6 +126,7 @@ export function setupFeishuHandlers(config, sessionManager) {
|
|
|
125
126
|
const accessControl = new AccessControl(config.feishuAllowedUserIds);
|
|
126
127
|
const requestQueue = new RequestQueue();
|
|
127
128
|
const runningTasks = new Map();
|
|
129
|
+
const stopTaskCleanup = startTaskCleanup(runningTasks);
|
|
128
130
|
const commandHandler = new CommandHandler({
|
|
129
131
|
config,
|
|
130
132
|
sessionManager,
|
|
@@ -606,7 +608,7 @@ export function setupFeishuHandlers(config, sessionManager) {
|
|
|
606
608
|
}
|
|
607
609
|
}
|
|
608
610
|
return {
|
|
609
|
-
stop: () =>
|
|
611
|
+
stop: () => stopTaskCleanup(),
|
|
610
612
|
getRunningTaskCount: () => runningTasks.size,
|
|
611
613
|
handleEvent,
|
|
612
614
|
};
|
package/dist/qq/event-handler.js
CHANGED
|
@@ -5,6 +5,7 @@ import { sendThinkingMessage, updateMessage, sendFinalMessages, sendErrorMessage
|
|
|
5
5
|
import { CommandHandler } from "../commands/handler.js";
|
|
6
6
|
import { getAdapter } from "../adapters/registry.js";
|
|
7
7
|
import { runAITask } from "../shared/ai-task.js";
|
|
8
|
+
import { startTaskCleanup } from "../shared/task-cleanup.js";
|
|
8
9
|
import { setActiveChatId } from "../shared/active-chats.js";
|
|
9
10
|
import { setChatUser } from "../shared/chat-user-map.js";
|
|
10
11
|
import { createLogger } from "../logger.js";
|
|
@@ -118,6 +119,7 @@ export function setupQQHandlers(config, sessionManager) {
|
|
|
118
119
|
const runningTasks = new Map();
|
|
119
120
|
const recentEventIds = new Map();
|
|
120
121
|
const recentEventFingerprints = new Map();
|
|
122
|
+
const stopTaskCleanup = startTaskCleanup(runningTasks);
|
|
121
123
|
const commandHandler = new CommandHandler({
|
|
122
124
|
config,
|
|
123
125
|
sessionManager,
|
|
@@ -256,7 +258,7 @@ export function setupQQHandlers(config, sessionManager) {
|
|
|
256
258
|
log.info(`QQ message handled: user=${userId}, chat=${chatId}, status=${enqueueResult}, attachments=${event.attachments?.length ?? 0}`);
|
|
257
259
|
}
|
|
258
260
|
return {
|
|
259
|
-
stop: () =>
|
|
261
|
+
stop: () => stopTaskCleanup(),
|
|
260
262
|
getRunningTaskCount: () => runningTasks.size,
|
|
261
263
|
handleEvent,
|
|
262
264
|
};
|
package/dist/setup.js
CHANGED
|
@@ -75,10 +75,11 @@ function printManualInstructions(configPath) {
|
|
|
75
75
|
"tools": {
|
|
76
76
|
"claude": {
|
|
77
77
|
"cliPath": "claude",
|
|
78
|
-
"workDir": "${process.cwd().replace(/\\/g, "/")}"
|
|
78
|
+
"workDir": "${process.cwd().replace(/\\/g, "/")}",
|
|
79
|
+
"timeoutMs": 600000
|
|
79
80
|
},
|
|
80
81
|
"codex": { "cliPath": "codex", "workDir": "${process.cwd().replace(/\\/g, "/")}", "proxy": "http://127.0.0.1:7890" },
|
|
81
|
-
"codebuddy": { "cliPath": "codebuddy" }
|
|
82
|
+
"codebuddy": { "cliPath": "codebuddy", "timeoutMs": 600000 }
|
|
82
83
|
},
|
|
83
84
|
"platforms": {
|
|
84
85
|
"telegram": {
|
|
@@ -861,6 +862,7 @@ export async function runInteractiveSetup() {
|
|
|
861
862
|
...baseTools.claude,
|
|
862
863
|
cliPath: baseTools.claude?.cliPath ?? "claude",
|
|
863
864
|
workDir,
|
|
865
|
+
timeoutMs: baseTools.claude?.timeoutMs ?? 600000,
|
|
864
866
|
},
|
|
865
867
|
codex: {
|
|
866
868
|
...baseTools.codex,
|
|
@@ -873,6 +875,7 @@ export async function runInteractiveSetup() {
|
|
|
873
875
|
codebuddy: {
|
|
874
876
|
...baseTools.codebuddy,
|
|
875
877
|
cliPath: baseTools.codebuddy?.cliPath ?? "codebuddy",
|
|
878
|
+
timeoutMs: baseTools.codebuddy?.timeoutMs ?? 600000,
|
|
876
879
|
},
|
|
877
880
|
},
|
|
878
881
|
};
|
package/dist/shared/ai-task.js
CHANGED
|
@@ -86,6 +86,11 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
|
|
|
86
86
|
// 使用 aiCommand 而不是 toolAdapter.toolId,确保 sessionId 的存储和查询使用相同的 key
|
|
87
87
|
const aiCommand = resolvePlatformAiCommand(config, ctx.platform);
|
|
88
88
|
const toolId = toolAdapter.toolId;
|
|
89
|
+
const timeoutMs = aiCommand === 'codex'
|
|
90
|
+
? config.codexTimeoutMs
|
|
91
|
+
: aiCommand === 'codebuddy'
|
|
92
|
+
? config.codebuddyTimeoutMs
|
|
93
|
+
: config.claudeTimeoutMs;
|
|
89
94
|
const startRun = () => {
|
|
90
95
|
log.info(`[AITask] Starting: userId=${ctx.userId}, initialSessionId=${currentSessionId ?? 'new'}, prompt="${prompt.slice(0, 50)}..."`);
|
|
91
96
|
activeHandle = toolAdapter.run(prompt, currentSessionId, ctx.workDir, {
|
|
@@ -220,6 +225,7 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
|
|
|
220
225
|
resolve();
|
|
221
226
|
},
|
|
222
227
|
}, {
|
|
228
|
+
timeoutMs,
|
|
223
229
|
model: sessionManager.getModel(ctx.userId, ctx.threadId) ?? config.claudeModel,
|
|
224
230
|
chatId: ctx.chatId,
|
|
225
231
|
// 默认跳过权限确认,保持全自动执行
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createLogger } from '../logger.js';
|
|
2
|
+
const log = createLogger('TaskCleanup');
|
|
3
|
+
const TASK_TIMEOUT_MS = 30 * 60 * 1000;
|
|
4
|
+
const INTERVAL_MS = 10 * 60 * 1000;
|
|
5
|
+
export function startTaskCleanup(runningTasks) {
|
|
6
|
+
const timer = setInterval(() => {
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
for (const [key, task] of runningTasks) {
|
|
9
|
+
if (now - task.startedAt > TASK_TIMEOUT_MS) {
|
|
10
|
+
log.warn(`Auto-cleaning timeout task: ${key}`);
|
|
11
|
+
task.settle();
|
|
12
|
+
task.handle.abort();
|
|
13
|
+
runningTasks.delete(key);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}, INTERVAL_MS);
|
|
17
|
+
timer.unref();
|
|
18
|
+
return () => clearInterval(timer);
|
|
19
|
+
}
|
|
@@ -6,6 +6,7 @@ import { sendThinkingMessage, updateMessage, sendFinalMessages, sendTextReply, s
|
|
|
6
6
|
import { CommandHandler } from "../commands/handler.js";
|
|
7
7
|
import { getAdapter } from "../adapters/registry.js";
|
|
8
8
|
import { runAITask } from "../shared/ai-task.js";
|
|
9
|
+
import { startTaskCleanup } from "../shared/task-cleanup.js";
|
|
9
10
|
import { TELEGRAM_THROTTLE_MS } from "../constants.js";
|
|
10
11
|
import { setActiveChatId } from "../shared/active-chats.js";
|
|
11
12
|
import { setChatUser } from "../shared/chat-user-map.js";
|
|
@@ -65,6 +66,7 @@ export function setupTelegramHandlers(bot, config, sessionManager) {
|
|
|
65
66
|
const accessControl = new AccessControl(config.telegramAllowedUserIds);
|
|
66
67
|
const requestQueue = new RequestQueue();
|
|
67
68
|
const runningTasks = new Map();
|
|
69
|
+
const stopTaskCleanup = startTaskCleanup(runningTasks);
|
|
68
70
|
const commandHandler = new CommandHandler({
|
|
69
71
|
config,
|
|
70
72
|
sessionManager,
|
|
@@ -486,7 +488,7 @@ export function setupTelegramHandlers(bot, config, sessionManager) {
|
|
|
486
488
|
}
|
|
487
489
|
});
|
|
488
490
|
return {
|
|
489
|
-
stop: () =>
|
|
491
|
+
stop: () => stopTaskCleanup(),
|
|
490
492
|
getRunningTaskCount: () => runningTasks.size,
|
|
491
493
|
};
|
|
492
494
|
}
|
|
@@ -8,6 +8,7 @@ import { sendThinkingMessage, updateMessage, sendFinalMessages, sendTextReply, s
|
|
|
8
8
|
import { CommandHandler } from '../commands/handler.js';
|
|
9
9
|
import { getAdapter } from '../adapters/registry.js';
|
|
10
10
|
import { runAITask } from '../shared/ai-task.js';
|
|
11
|
+
import { startTaskCleanup } from '../shared/task-cleanup.js';
|
|
11
12
|
import { WEWORK_THROTTLE_MS } from '../constants.js';
|
|
12
13
|
import { setActiveChatId } from '../shared/active-chats.js';
|
|
13
14
|
import { setChatUser } from '../shared/chat-user-map.js';
|
|
@@ -162,6 +163,7 @@ export function setupWeWorkHandlers(config, sessionManager) {
|
|
|
162
163
|
const accessControl = new AccessControl(config.weworkAllowedUserIds);
|
|
163
164
|
const requestQueue = new RequestQueue();
|
|
164
165
|
const runningTasks = new Map();
|
|
166
|
+
const stopTaskCleanup = startTaskCleanup(runningTasks);
|
|
165
167
|
// Mutable ref that captures the req_id of the message currently being handled.
|
|
166
168
|
// WeWork requires req_id to reply; CommandHandler doesn't carry it, so we inject
|
|
167
169
|
// it via a closure. WeWork delivers messages sequentially over WebSocket, so
|
|
@@ -330,7 +332,7 @@ export function setupWeWorkHandlers(config, sessionManager) {
|
|
|
330
332
|
}
|
|
331
333
|
}
|
|
332
334
|
return {
|
|
333
|
-
stop: () =>
|
|
335
|
+
stop: () => stopTaskCleanup(),
|
|
334
336
|
getRunningTaskCount: () => runningTasks.size,
|
|
335
337
|
handleEvent,
|
|
336
338
|
};
|
|
@@ -8,6 +8,7 @@ import { sendTextReply, sendErrorReply } from './message-sender.js';
|
|
|
8
8
|
import { CommandHandler } from '../commands/handler.js';
|
|
9
9
|
import { getAdapter } from '../adapters/registry.js';
|
|
10
10
|
import { runAITask } from '../shared/ai-task.js';
|
|
11
|
+
import { startTaskCleanup } from '../shared/task-cleanup.js';
|
|
11
12
|
import { WORKBUDDY_THROTTLE_MS } from '../constants.js';
|
|
12
13
|
import { setActiveChatId } from '../shared/active-chats.js';
|
|
13
14
|
import { setChatUser } from '../shared/chat-user-map.js';
|
|
@@ -18,6 +19,7 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
|
|
|
18
19
|
const requestQueue = new RequestQueue();
|
|
19
20
|
const runningTasks = new Map();
|
|
20
21
|
const taskKeyByChatId = new Map();
|
|
22
|
+
const stopTaskCleanup = startTaskCleanup(runningTasks);
|
|
21
23
|
// Base dependencies for creating per-event CommandHandler
|
|
22
24
|
const baseCommandDeps = {
|
|
23
25
|
config,
|
|
@@ -114,7 +116,7 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
|
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
return {
|
|
117
|
-
stop: () =>
|
|
119
|
+
stop: () => stopTaskCleanup(),
|
|
118
120
|
getRunningTaskCount: () => runningTasks.size,
|
|
119
121
|
handleEvent,
|
|
120
122
|
};
|