ylib-syim 0.0.6 → 0.0.7

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/bridges/logger.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { format } from "node:util";
4
+ import type { WriteStream } from "node:fs";
4
5
 
5
6
  type ConsoleMethod = (...args: unknown[]) => void;
6
7
 
@@ -27,21 +28,130 @@ function patchConsole(writeLine: (line: string) => void): void {
27
28
  const original = console[method] as ConsoleMethod;
28
29
  console[method] = (...args: unknown[]) => {
29
30
  const line = format(...args);
30
- writeLine(`[${new Date().toISOString()}] [${method}] ${line}`);
31
+ try {
32
+ writeLine(`[${new Date().toISOString()}] [${method}] ${line}`);
33
+ } catch {
34
+ // logger 必须永不影响主进程
35
+ }
31
36
  original(...args);
32
37
  };
33
38
  }
34
39
  }
35
40
 
41
+ function parsePositiveInt(value: unknown, fallback: number): number {
42
+ const n = Number(String(value ?? "").trim());
43
+ if (!Number.isFinite(n)) return fallback;
44
+ if (n <= 0) return fallback;
45
+ return Math.floor(n);
46
+ }
47
+
36
48
  export function setupBridgeLogger(prefix: string): string {
37
49
  const exists = globalThis.__imAgentHubBridgeLogger;
38
50
  if (exists?.enabled) return exists.logFilePath;
39
51
 
40
- const logFilePath = buildLogFilePath(prefix);
41
- const stream = fs.createWriteStream(logFilePath, { flags: "a" });
42
- stream.write(`[${new Date().toISOString()}] [system] bridge logger started\n`);
52
+ try {
53
+ const logFilePath = buildLogFilePath(prefix);
54
+ const maxLines = parsePositiveInt(
55
+ process.env.IM_AGENT_HUB_LOG_MAX_LINES,
56
+ 20000,
57
+ );
58
+ const trimBatch = parsePositiveInt(
59
+ process.env.IM_AGENT_HUB_LOG_TRIM_BATCH,
60
+ 1000,
61
+ );
62
+
63
+ let stream: WriteStream = fs.createWriteStream(logFilePath, { flags: "a" });
64
+ stream.on("error", () => {
65
+ // logger 必须永不影响主进程;出现 stream 错误后,后续写入直接丢弃
66
+ });
67
+ let streamBroken = false;
68
+ stream.on("error", () => {
69
+ streamBroken = true;
70
+ });
71
+ let writtenLines = 0;
72
+ let trimming = false;
73
+ let paused = false;
74
+ const buffer: string[] = [];
75
+
76
+ function writeRaw(lineWithNewline: string): void {
77
+ try {
78
+ if (streamBroken) return;
79
+ if (paused) {
80
+ buffer.push(lineWithNewline);
81
+ return;
82
+ }
83
+ stream.write(lineWithNewline);
84
+ } catch {
85
+ // swallow
86
+ }
87
+ }
43
88
 
44
- patchConsole((line) => stream.write(`${line}\n`));
45
- globalThis.__imAgentHubBridgeLogger = { enabled: true, logFilePath };
46
- return logFilePath;
89
+ async function trimToLastLines(): Promise<void> {
90
+ if (trimming) return;
91
+ trimming = true;
92
+ paused = true;
93
+ try {
94
+ await new Promise<void>((resolve) => {
95
+ try {
96
+ stream.end(() => resolve());
97
+ } catch {
98
+ resolve();
99
+ }
100
+ });
101
+ const text = fs.readFileSync(logFilePath, "utf-8");
102
+ const lines = text.split("\n");
103
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
104
+ const kept = lines.slice(-maxLines);
105
+ fs.writeFileSync(
106
+ logFilePath,
107
+ kept.join("\n") + (kept.length ? "\n" : ""),
108
+ "utf-8",
109
+ );
110
+ stream = fs.createWriteStream(logFilePath, { flags: "a" });
111
+ streamBroken = false;
112
+ stream.on("error", () => {
113
+ streamBroken = true;
114
+ });
115
+ for (const item of buffer) {
116
+ try {
117
+ if (streamBroken) break;
118
+ stream.write(item);
119
+ } catch {
120
+ break;
121
+ }
122
+ }
123
+ buffer.length = 0;
124
+ writtenLines = Math.min(maxLines, kept.length);
125
+ } catch {
126
+ try {
127
+ stream = fs.createWriteStream(logFilePath, { flags: "a" });
128
+ streamBroken = false;
129
+ stream.on("error", () => {
130
+ streamBroken = true;
131
+ });
132
+ } catch {
133
+ streamBroken = true;
134
+ }
135
+ } finally {
136
+ paused = false;
137
+ trimming = false;
138
+ }
139
+ }
140
+
141
+ writeRaw(`[${new Date().toISOString()}] [system] bridge logger started\n`);
142
+ writtenLines += 1;
143
+
144
+ patchConsole((line) => {
145
+ writeRaw(`${line}\n`);
146
+ writtenLines += 1;
147
+ if (maxLines > 0 && writtenLines > maxLines + trimBatch && !trimming) {
148
+ void trimToLastLines();
149
+ }
150
+ });
151
+ globalThis.__imAgentHubBridgeLogger = { enabled: true, logFilePath };
152
+ return logFilePath;
153
+ } catch {
154
+ globalThis.__imAgentHubBridgeLogger = { enabled: false, logFilePath: "" };
155
+ return "";
156
+ }
47
157
  }
package/bridges/main.ts CHANGED
@@ -971,14 +971,18 @@ async function pullRuntimeConfigFromPython(forceFull = false): Promise<{
971
971
  function startRuntimeConfigPollLoop(): void {
972
972
  if (!runtimeConfigPullUrl) return;
973
973
  if (runtimeConfigPollIntervalMs <= 0) return;
974
+ let recoveryHandled = false;
974
975
  const timer = setInterval(() => {
975
976
  void pullRuntimeConfigFromPython(false).then((result) => {
976
- if (result.ok) {
977
- console.log(
978
- "[bridges/main] poll pull recovered success, stop polling and wait admin restart/start for next config apply",
979
- );
980
- clearInterval(timer);
981
- }
977
+ if (!result.ok) return;
978
+ if (recoveryHandled) return;
979
+ recoveryHandled = true;
980
+ console.log(
981
+ "[bridges/main] poll pull recovered success, stop polling and exit for hard restart",
982
+ );
983
+ clearInterval(timer);
984
+ // 配置源(Python)恢复后硬重启:退出进程,由 PM2/系统守护重新拉起,下次启动走正常拉配置与起 bridge 流程。
985
+ setTimeout(() => process.exit(0), 100);
982
986
  });
983
987
  }, runtimeConfigPollIntervalMs);
984
988
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ylib-syim",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "多 IM / 多 Agent 的会话路由与上下文管理(支持 /new)",
5
5
  "type": "module",
6
6
  "exports": {
@@ -41,7 +41,7 @@
41
41
  "dependencies": {
42
42
  "@ffmpeg-installer/ffmpeg": "^1.1.0",
43
43
  "ylib-dingtalk-connector": "0.7.10-beata.2",
44
- "ylib-openclaw-lark": "2026.3.17-beata.5",
44
+ "ylib-openclaw-lark": "2026.3.17-beata.6",
45
45
  "axios": "^1.6.0",
46
46
  "dingtalk-stream": "^2.1.4",
47
47
  "fluent-ffmpeg": "^2.1.3",