@wu529778790/open-im 1.11.9-beta.7 → 1.11.10-beta.0

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.
@@ -64,6 +64,11 @@ export interface Config {
64
64
  shortRetrySeconds: number;
65
65
  autoResumePrompt: string;
66
66
  };
67
+ keepalive?: {
68
+ enabled: boolean;
69
+ intervalHours: number;
70
+ target: AiCommand;
71
+ };
67
72
  platforms: {
68
73
  telegram?: {
69
74
  enabled: boolean;
@@ -211,6 +216,11 @@ export interface FileToolOpenCode {
211
216
  /** 模型(格式: providerID/modelID) */
212
217
  model?: string;
213
218
  }
219
+ export interface KeepaliveConfig {
220
+ enabled?: boolean;
221
+ intervalHours?: number;
222
+ target?: AiCommand;
223
+ }
214
224
  export interface FileConfig {
215
225
  telegramBotToken?: string;
216
226
  feishuAppId?: string;
@@ -241,4 +251,5 @@ export interface FileConfig {
241
251
  url?: string;
242
252
  token?: string;
243
253
  };
254
+ keepalive?: KeepaliveConfig;
244
255
  }
@@ -421,6 +421,29 @@ export async function startWebConfigServer(options) {
421
421
  }
422
422
  return;
423
423
  }
424
+ // --- 保活设置 ---
425
+ if (request.method === "GET" && requestUrl.pathname === "/api/keepalive/config") {
426
+ try {
427
+ const { getKeepaliveConfig } = await import("./shared/keepalive.js");
428
+ json(response, 200, getKeepaliveConfig(), request);
429
+ }
430
+ catch (error) {
431
+ json(response, 500, { error: error instanceof Error ? error.message : String(error) }, request);
432
+ }
433
+ return;
434
+ }
435
+ if (request.method === "POST" && requestUrl.pathname === "/api/keepalive/config") {
436
+ try {
437
+ const body = await readJson(request);
438
+ const { saveKeepaliveConfig } = await import("./shared/keepalive.js");
439
+ saveKeepaliveConfig(body);
440
+ json(response, 200, { message: "保活配置已保存" }, request);
441
+ }
442
+ catch (error) {
443
+ json(response, 500, { error: error instanceof Error ? error.message : String(error) }, request);
444
+ }
445
+ return;
446
+ }
424
447
  if (request.method === "GET" && requestUrl.pathname === "/api/health") {
425
448
  const file = loadFileConfig();
426
449
  const platforms = getHealthPlatformSnapshot(file);
package/dist/config.js CHANGED
@@ -519,6 +519,13 @@ export function loadConfig() {
519
519
  apiToken: clawbotApiToken,
520
520
  },
521
521
  };
522
+ // 保活配置
523
+ const keepaliveFile = file.keepalive ?? {};
524
+ const keepalive = {
525
+ enabled: keepaliveFile.enabled ?? true,
526
+ intervalHours: keepaliveFile.intervalHours ?? 5,
527
+ target: keepaliveFile.target ?? 'claude',
528
+ };
522
529
  return {
523
530
  enabledPlatforms,
524
531
  telegramBotToken: telegramBotToken ?? '',
@@ -556,6 +563,7 @@ export function loadConfig() {
556
563
  enabled: telemetryEnabled,
557
564
  },
558
565
  autopilot,
566
+ keepalive,
559
567
  platforms,
560
568
  };
561
569
  }
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ import { setupClawbotHandlers } from "./clawbot/event-handler.js";
29
29
  import { sendTextReply as sendClawbotTextReply } from "./clawbot/message-sender.js";
30
30
  import { initClawBotSender } from "./clawbot/message-sender.js";
31
31
  import { initAdapters, cleanupAdapters } from "./adapters/registry.js";
32
+ import { startKeepalive, stopKeepalive } from "./shared/keepalive.js";
32
33
  import { SessionManager } from "./session/session-manager.js";
33
34
  import { loadActiveChats, getActiveChatId, flushActiveChats, } from "./shared/active-chats.js";
34
35
  import { destroyAllLiveChildren } from "./shared/process-kill.js";
@@ -271,6 +272,7 @@ export async function main() {
271
272
  }
272
273
  loadActiveChats();
273
274
  initAdapters(config);
275
+ startKeepalive();
274
276
  // 尽早启动 shutdown 并写入 port 文件,使 open-im start 的 8s 就绪超时能通过(平台初始化可能较慢)
275
277
  let shutdownServer = null;
276
278
  await new Promise((resolve, reject) => {
@@ -386,6 +388,7 @@ export async function main() {
386
388
  }
387
389
  sessionManager.destroy();
388
390
  cleanupAdapters();
391
+ stopKeepalive();
389
392
  flushActiveChats();
390
393
  await flushSentry();
391
394
  await closeLogger();
@@ -0,0 +1,16 @@
1
+ /** 启动保活定时器 */
2
+ export declare function startKeepalive(): void;
3
+ /** 停止保活定时器 */
4
+ export declare function stopKeepalive(): void;
5
+ /** 从 API 读取保活配置(返回给前端) */
6
+ export declare function getKeepaliveConfig(): {
7
+ enabled: boolean;
8
+ intervalHours: number;
9
+ target: string;
10
+ };
11
+ /** 从 API 保存保活配置 */
12
+ export declare function saveKeepaliveConfig(cfg: {
13
+ enabled: boolean;
14
+ intervalHours: number;
15
+ target: string;
16
+ }): void;
@@ -0,0 +1,88 @@
1
+ import { loadFileConfig, saveFileConfig } from "../config/file-io.js";
2
+ import { loadConfig } from "../config.js";
3
+ import { createLogger } from "../logger.js";
4
+ const log = createLogger("Keepalive");
5
+ let timer = null;
6
+ /** 对 Claude SDK 执行一次保活请求 */
7
+ async function pingClaude(workDir) {
8
+ try {
9
+ const { ClaudeSDKAdapter } = await import("../adapters/claude-sdk-adapter.js");
10
+ const info = await ClaudeSDKAdapter.getAccountInfo(workDir);
11
+ if (info) {
12
+ const str = JSON.stringify(info).substring(0, 100);
13
+ log.info(`Keepalive ping OK: ${str}`);
14
+ return true;
15
+ }
16
+ log.warn("Keepalive ping returned no account info");
17
+ return false;
18
+ }
19
+ catch (err) {
20
+ log.warn(`Keepalive ping failed: ${err}`);
21
+ return false;
22
+ }
23
+ }
24
+ /** 通用保活 ping */
25
+ async function ping(target, workDir) {
26
+ switch (target) {
27
+ case "claude":
28
+ return pingClaude(workDir);
29
+ // 其他 AI 工具后续可按需实现
30
+ default:
31
+ log.info(`Keepalive: ${target} 的保活尚未实现,跳过`);
32
+ return false;
33
+ }
34
+ }
35
+ /** 启动保活定时器 */
36
+ export function startKeepalive() {
37
+ stopKeepalive(); // 确保不重复
38
+ const file = loadFileConfig();
39
+ const ka = file.keepalive ?? {};
40
+ const enabled = ka.enabled ?? true;
41
+ const intervalHours = ka.intervalHours ?? 5;
42
+ const target = ka.target ?? "claude";
43
+ if (!enabled) {
44
+ log.info("Keepalive 已禁用,跳过");
45
+ return;
46
+ }
47
+ const config = loadConfig();
48
+ const workDir = config.claudeWorkDir;
49
+ const intervalMs = intervalHours * 60 * 60 * 1000;
50
+ log.info(`Keepalive 已启动,每 ${intervalHours} 小时 ping ${target}`);
51
+ // 立即执行一次
52
+ ping(target, workDir);
53
+ // 定时执行
54
+ timer = setInterval(() => {
55
+ ping(target, workDir);
56
+ }, intervalMs);
57
+ timer.unref?.(); // 不阻止进程退出
58
+ }
59
+ /** 停止保活定时器 */
60
+ export function stopKeepalive() {
61
+ if (timer) {
62
+ clearInterval(timer);
63
+ timer = null;
64
+ log.info("Keepalive 已停止");
65
+ }
66
+ }
67
+ /** 从 API 读取保活配置(返回给前端) */
68
+ export function getKeepaliveConfig() {
69
+ const file = loadFileConfig();
70
+ const ka = file.keepalive ?? {};
71
+ return {
72
+ enabled: ka.enabled ?? true,
73
+ intervalHours: ka.intervalHours ?? 5,
74
+ target: ka.target ?? "claude",
75
+ };
76
+ }
77
+ /** 从 API 保存保活配置 */
78
+ export function saveKeepaliveConfig(cfg) {
79
+ const file = loadFileConfig();
80
+ file.keepalive = {
81
+ enabled: cfg.enabled,
82
+ intervalHours: cfg.intervalHours,
83
+ target: cfg.target,
84
+ };
85
+ saveFileConfig(file);
86
+ // 保存后立即重启定时器
87
+ startKeepalive();
88
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.11.9-beta.7",
3
+ "version": "1.11.10-beta.0",
4
4
  "description": "Your AI coding assistant, in every chat app. Multi-platform IM bridge for Claude Code, Codex, and CodeBuddy.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",