alemonjs 2.1.0-alpha.43 → 2.1.0-alpha.45

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.
Files changed (2) hide show
  1. package/lib/adapter.js +22 -106
  2. package/package.json +1 -1
package/lib/adapter.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { fork } from 'child_process';
2
2
  import { createRequire } from 'module';
3
- import { createInterface } from 'readline';
4
3
 
5
4
  const require = createRequire(import.meta.url);
6
5
  /**
@@ -10,59 +9,6 @@ const require = createRequire(import.meta.url);
10
9
  * @param env 环境变量对象
11
10
  * @param logger 日志对象(需实现 info/warn/error)
12
11
  */
13
- let currentChild;
14
- let cleanupRegistered = false;
15
- let shuttingDown = false; // 标记是否正在整体退出,避免触发重启
16
- function terminateChild(reason) {
17
- if (currentChild && !currentChild.killed) {
18
- try {
19
- logger?.debug?.(`即将终止平台连接子进程 pid=${currentChild.pid}${reason ? ' reason=' + reason : ''}`);
20
- // 先尝试发送一个 shutdown 指令(如果对端支持)
21
- try {
22
- currentChild.send?.(JSON.stringify({ type: 'shutdown' }));
23
- }
24
- catch { /* ignore */ }
25
- currentChild.removeAllListeners();
26
- currentChild.kill('SIGTERM');
27
- // 兜底强杀
28
- setTimeout(() => {
29
- if (currentChild && !currentChild.killed) {
30
- try {
31
- currentChild.kill('SIGKILL');
32
- }
33
- catch { /* ignore */ }
34
- }
35
- }, 3000).unref?.();
36
- }
37
- catch (e) {
38
- logger?.warn?.('终止子进程失败', e);
39
- }
40
- }
41
- currentChild = undefined;
42
- }
43
- function registerProcessCleanup() {
44
- if (cleanupRegistered) {
45
- return;
46
- }
47
- cleanupRegistered = true;
48
- const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
49
- const handle = (sig) => {
50
- shuttingDown = true;
51
- terminateChild(`parent ${sig}`);
52
- };
53
- signals.forEach(s => process.once(s, handle));
54
- process.once('beforeExit', () => handle('beforeExit'));
55
- process.once('exit', () => handle('exit'));
56
- // 如果父进程与其父断开(例如被主控杀掉),也尝试清理
57
- process.on('disconnect', () => {
58
- shuttingDown = true;
59
- terminateChild('parent disconnect');
60
- });
61
- }
62
- function stopAdapter() {
63
- shuttingDown = true;
64
- terminateChild('manual stop');
65
- }
66
12
  function startAdapterWithFallback() {
67
13
  let modulePath = '';
68
14
  try {
@@ -80,51 +26,52 @@ function startAdapterWithFallback() {
80
26
  }
81
27
  let restarted = false;
82
28
  let ready = false;
29
+ let child;
83
30
  const restart = () => {
84
31
  if (restarted || imported) {
85
32
  return;
86
33
  }
87
34
  restarted = true;
88
- terminateChild('restart');
35
+ if (child) {
36
+ child.removeAllListeners();
37
+ try {
38
+ child.kill();
39
+ }
40
+ catch { }
41
+ }
89
42
  setTimeout(() => {
90
43
  startByFork();
91
44
  }, 3000);
92
45
  };
93
46
  try {
94
- currentChild = fork(modulePath);
95
- registerProcessCleanup();
47
+ child = fork(modulePath);
96
48
  // 超时
97
49
  const checkTimeout = async () => {
98
50
  if (!ready && !imported) {
99
51
  logger?.warn?.('平台连接未及时响应(未发送 ready 消息),降级为 import 加载, 请升级对应的平台连接包以提高进程稳定性');
100
52
  try {
101
- currentChild?.kill();
53
+ child?.kill();
102
54
  }
103
55
  catch { }
104
56
  await startByImport();
105
57
  }
106
58
  };
107
59
  const timer = setTimeout(() => void checkTimeout(), 2000);
108
- currentChild.on('exit', (code, signal) => {
60
+ child.on('exit', (code, signal) => {
109
61
  clearTimeout(timer);
110
- if (shuttingDown) {
111
- logger?.debug?.('父进程正在关闭,忽略子进程退出重启逻辑');
112
- return;
113
- }
114
62
  if (!imported) {
115
63
  logger?.warn?.(`平台连接子进程已退出,code=${code}, signal=${signal},3秒后自动重启`);
116
64
  restart();
117
65
  }
118
66
  });
119
- currentChild.on('message', msg => {
67
+ child.on('message', msg => {
120
68
  try {
121
69
  const data = typeof msg === 'string' ? JSON.parse(msg) : msg;
122
70
  if (data?.type === 'ready') {
123
71
  ready = true;
124
72
  clearTimeout(timer);
125
73
  logger?.debug?.('平台连接已就绪(子进程 fork 模式)');
126
- // 发送启动
127
- currentChild?.send(JSON.stringify({ type: 'start' }));
74
+ child?.send?.({ type: 'start' });
128
75
  }
129
76
  }
130
77
  catch (err) {
@@ -162,45 +109,14 @@ function startAdapterWithFallback() {
162
109
  };
163
110
  startByFork();
164
111
  }
165
- // 若当前进程本身也是被上层 fork/spawn 的子进程,可接收父进程的 shutdown 指令并级联关闭其内部子进程。
166
- if (typeof process.send === 'function') {
167
- process.on('message', msg => {
168
- try {
169
- const data = typeof msg === 'string' ? JSON.parse(msg) : msg;
170
- if (data?.type === 'shutdown') {
171
- shuttingDown = true;
172
- stopAdapter();
173
- // 给父进程一个确认
174
- try {
175
- process.send?.(JSON.stringify({ type: 'shutdown-ack' }));
176
- }
177
- catch {
178
- // ignore
179
- }
180
- // 适当延迟确保子进程杀干净
181
- setTimeout(() => process.exit(0), 100);
182
- }
183
- }
184
- catch { /* ignore parse errors */ }
112
+ ['SIGINT', 'SIGTERM', 'SIGQUIT', 'disconnect'].forEach(sig => {
113
+ process?.on?.(sig, () => {
114
+ logger?.info?.(`[${sig}] 收到信号,正在关闭...`);
115
+ setImmediate(() => process.exit(0));
185
116
  });
186
- }
187
- // 兼容由 Go / 其它语言通过 exec 启动:向 stdin 写入 "shutdown"(换行) 触发优雅退出
188
- try {
189
- if (!process.stdin.destroyed) {
190
- const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
191
- rl.on('line', line => {
192
- const cmd = line.trim().toLowerCase();
193
- if (cmd === 'shutdown' || cmd === 'quit' || cmd === 'exit') {
194
- shuttingDown = true;
195
- stopAdapter();
196
- rl.close();
197
- setTimeout(() => process.exit(0), 50);
198
- }
199
- });
200
- // 不阻止进程退出
201
- rl.on('close', () => { });
202
- }
203
- }
204
- catch { /* ignore */ }
117
+ });
118
+ process?.on?.('exit', (code) => {
119
+ logger?.info?.(`[exit] 进程退出,code=${code}`);
120
+ });
205
121
 
206
- export { startAdapterWithFallback, stopAdapter };
122
+ export { startAdapterWithFallback };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alemonjs",
3
- "version": "2.1.0-alpha.43",
3
+ "version": "2.1.0-alpha.45",
4
4
  "description": "bot script",
5
5
  "author": "lemonade",
6
6
  "license": "MIT",