pikiloom 0.4.28 → 0.4.29
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/channels/dingtalk/bot.js +0 -1
- package/dist/channels/discord/bot.js +0 -1
- package/dist/channels/feishu/bot.js +0 -1
- package/dist/channels/slack/bot.js +0 -1
- package/dist/channels/telegram/bot.js +0 -1
- package/dist/channels/wecom/bot.js +0 -1
- package/dist/channels/weixin/bot.js +0 -1
- package/dist/core/process-control.js +76 -29
- package/dist/dashboard/routes/config.js +1 -9
- package/package.json +1 -1
|
@@ -521,7 +521,6 @@ export class DingtalkBot extends Bot {
|
|
|
521
521
|
this.processRuntimeCleanup?.();
|
|
522
522
|
this.processRuntimeCleanup = registerProcessRuntime({
|
|
523
523
|
label: 'dingtalk',
|
|
524
|
-
getActiveTaskCount: () => this.activeTasks.size,
|
|
525
524
|
prepareForRestart: () => this.cleanupRuntimeForExit(),
|
|
526
525
|
});
|
|
527
526
|
this.installSignalHandlers();
|
|
@@ -520,7 +520,6 @@ export class DiscordBot extends Bot {
|
|
|
520
520
|
this.processRuntimeCleanup?.();
|
|
521
521
|
this.processRuntimeCleanup = registerProcessRuntime({
|
|
522
522
|
label: 'discord',
|
|
523
|
-
getActiveTaskCount: () => this.activeTasks.size,
|
|
524
523
|
prepareForRestart: () => this.cleanupRuntimeForExit(),
|
|
525
524
|
});
|
|
526
525
|
this.installSignalHandlers();
|
|
@@ -1151,7 +1151,6 @@ export class FeishuBot extends Bot {
|
|
|
1151
1151
|
this.processRuntimeCleanup?.();
|
|
1152
1152
|
this.processRuntimeCleanup = registerProcessRuntime({
|
|
1153
1153
|
label: 'feishu',
|
|
1154
|
-
getActiveTaskCount: () => this.activeTasks.size,
|
|
1155
1154
|
prepareForRestart: () => this.cleanupRuntimeForExit(),
|
|
1156
1155
|
buildRestartEnv: () => this.buildRestartEnv(),
|
|
1157
1156
|
});
|
|
@@ -522,7 +522,6 @@ export class SlackBot extends Bot {
|
|
|
522
522
|
this.processRuntimeCleanup?.();
|
|
523
523
|
this.processRuntimeCleanup = registerProcessRuntime({
|
|
524
524
|
label: 'slack',
|
|
525
|
-
getActiveTaskCount: () => this.activeTasks.size,
|
|
526
525
|
prepareForRestart: () => this.cleanupRuntimeForExit(),
|
|
527
526
|
});
|
|
528
527
|
this.installSignalHandlers();
|
|
@@ -1189,7 +1189,6 @@ export class TelegramBot extends Bot {
|
|
|
1189
1189
|
this.processRuntimeCleanup?.();
|
|
1190
1190
|
this.processRuntimeCleanup = registerProcessRuntime({
|
|
1191
1191
|
label: 'telegram',
|
|
1192
|
-
getActiveTaskCount: () => this.activeTasks.size,
|
|
1193
1192
|
prepareForRestart: () => this.cleanupRuntimeForExit(),
|
|
1194
1193
|
buildRestartEnv: () => this.buildRestartEnv(),
|
|
1195
1194
|
});
|
|
@@ -530,7 +530,6 @@ export class WeComBot extends Bot {
|
|
|
530
530
|
this.processRuntimeCleanup?.();
|
|
531
531
|
this.processRuntimeCleanup = registerProcessRuntime({
|
|
532
532
|
label: 'wecom',
|
|
533
|
-
getActiveTaskCount: () => this.activeTasks.size,
|
|
534
533
|
prepareForRestart: () => this.cleanupRuntimeForExit(),
|
|
535
534
|
});
|
|
536
535
|
this.installSignalHandlers();
|
|
@@ -886,7 +886,6 @@ export class WeixinBot extends Bot {
|
|
|
886
886
|
this.processRuntimeCleanup?.();
|
|
887
887
|
this.processRuntimeCleanup = registerProcessRuntime({
|
|
888
888
|
label: 'weixin',
|
|
889
|
-
getActiveTaskCount: () => this.activeTasks.size,
|
|
890
889
|
prepareForRestart: () => this.cleanupRuntimeForExit(),
|
|
891
890
|
});
|
|
892
891
|
this.installSignalHandlers();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { spawn } from 'node:child_process';
|
|
4
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
5
5
|
import { pathContainsSegment } from './platform.js';
|
|
6
6
|
import { STATE_DIR_NAME } from './constants.js';
|
|
7
7
|
export const PROCESS_RESTART_EXIT_CODE = 75;
|
|
@@ -118,12 +118,77 @@ export function registerProcessRuntime(runtime) {
|
|
|
118
118
|
export function getRegisteredRuntimeCount() {
|
|
119
119
|
return runtimes.size;
|
|
120
120
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
function readProcessParentMap() {
|
|
122
|
+
const children = new Map();
|
|
123
|
+
try {
|
|
124
|
+
const rows = [];
|
|
125
|
+
if (process.platform === 'win32') {
|
|
126
|
+
const out = execFileSync('wmic', ['process', 'get', 'ParentProcessId,ProcessId'], { encoding: 'utf8', windowsHide: true });
|
|
127
|
+
for (const line of out.split(/\r?\n/)) {
|
|
128
|
+
const m = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
129
|
+
if (m)
|
|
130
|
+
rows.push({ ppid: Number(m[1]), pid: Number(m[2]) });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const out = execFileSync('ps', ['-Ao', 'pid=,ppid='], { encoding: 'utf8' });
|
|
135
|
+
for (const line of out.split('\n')) {
|
|
136
|
+
const m = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
137
|
+
if (m)
|
|
138
|
+
rows.push({ pid: Number(m[1]), ppid: Number(m[2]) });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const { pid, ppid } of rows) {
|
|
142
|
+
const list = children.get(ppid);
|
|
143
|
+
if (list)
|
|
144
|
+
list.push(pid);
|
|
145
|
+
else
|
|
146
|
+
children.set(ppid, [pid]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
return children;
|
|
151
|
+
}
|
|
152
|
+
function collectDescendantPids(rootPid) {
|
|
153
|
+
const children = readProcessParentMap();
|
|
154
|
+
const descendants = [];
|
|
155
|
+
const seen = new Set([rootPid]);
|
|
156
|
+
const stack = [rootPid];
|
|
157
|
+
while (stack.length) {
|
|
158
|
+
const current = stack.pop();
|
|
159
|
+
for (const child of children.get(current) || []) {
|
|
160
|
+
if (seen.has(child))
|
|
161
|
+
continue;
|
|
162
|
+
seen.add(child);
|
|
163
|
+
descendants.push(child);
|
|
164
|
+
stack.push(child);
|
|
165
|
+
}
|
|
125
166
|
}
|
|
126
|
-
return
|
|
167
|
+
return descendants;
|
|
168
|
+
}
|
|
169
|
+
export async function killChildProcesses(rootPid = process.pid, opts = {}) {
|
|
170
|
+
const pids = collectDescendantPids(rootPid);
|
|
171
|
+
if (!pids.length)
|
|
172
|
+
return 0;
|
|
173
|
+
opts.log?.(`restart: terminating ${pids.length} child process(es) before restart`);
|
|
174
|
+
for (const pid of pids) {
|
|
175
|
+
try {
|
|
176
|
+
process.kill(pid, 'SIGTERM');
|
|
177
|
+
}
|
|
178
|
+
catch { }
|
|
179
|
+
}
|
|
180
|
+
const graceMs = opts.graceMs ?? 1500;
|
|
181
|
+
if (graceMs > 0)
|
|
182
|
+
await new Promise(resolve => setTimeout(resolve, graceMs));
|
|
183
|
+
for (const pid of pids) {
|
|
184
|
+
if (isProcessAlive(pid)) {
|
|
185
|
+
try {
|
|
186
|
+
process.kill(pid, 'SIGKILL');
|
|
187
|
+
}
|
|
188
|
+
catch { }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return pids.length;
|
|
127
192
|
}
|
|
128
193
|
export function createRestartStateFilePath(ownerPid = process.pid) {
|
|
129
194
|
const dir = path.join(os.tmpdir(), 'pikiloom');
|
|
@@ -223,21 +288,7 @@ function spawnReplacementProcess(bin, args, env, log) {
|
|
|
223
288
|
}
|
|
224
289
|
export async function requestProcessRestart(opts = {}) {
|
|
225
290
|
if (restartInFlight) {
|
|
226
|
-
return {
|
|
227
|
-
ok: true,
|
|
228
|
-
restarting: true,
|
|
229
|
-
error: null,
|
|
230
|
-
activeTasks: 0,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
const activeTasks = getActiveTaskCount();
|
|
234
|
-
if (activeTasks > 0) {
|
|
235
|
-
return {
|
|
236
|
-
ok: false,
|
|
237
|
-
restarting: false,
|
|
238
|
-
error: `${activeTasks} task(s) still running. Wait for them to finish or try again.`,
|
|
239
|
-
activeTasks,
|
|
240
|
-
};
|
|
291
|
+
return { ok: true, restarting: true, error: null };
|
|
241
292
|
}
|
|
242
293
|
restartInFlight = true;
|
|
243
294
|
const log = opts.log;
|
|
@@ -245,6 +296,7 @@ export async function requestProcessRestart(opts = {}) {
|
|
|
245
296
|
try {
|
|
246
297
|
const extraEnv = collectRestartEnv();
|
|
247
298
|
await prepareRuntimesForRestart(log);
|
|
299
|
+
await killChildProcesses(process.pid, { log });
|
|
248
300
|
if (process.env.PIKILOOM_DAEMON_CHILD === '1') {
|
|
249
301
|
const restartStateFile = process.env[PROCESS_RESTART_STATE_FILE_ENV];
|
|
250
302
|
if (restartStateFile) {
|
|
@@ -255,22 +307,17 @@ export async function requestProcessRestart(opts = {}) {
|
|
|
255
307
|
}
|
|
256
308
|
log?.('restart: handing off to daemon supervisor');
|
|
257
309
|
exit(PROCESS_RESTART_EXIT_CODE);
|
|
258
|
-
return { ok: true, restarting: true, error: null
|
|
310
|
+
return { ok: true, restarting: true, error: null };
|
|
259
311
|
}
|
|
260
312
|
const { bin, args } = buildRestartCommand(opts.argv || process.argv.slice(2), opts.restartCmd);
|
|
261
313
|
log?.(`restart: spawning \`${bin} ${args.join(' ')}\``);
|
|
262
314
|
spawnReplacementProcess(bin, args, buildRestartEnvForSpawn(extraEnv), log);
|
|
263
315
|
exit(0);
|
|
264
|
-
return { ok: true, restarting: true, error: null
|
|
316
|
+
return { ok: true, restarting: true, error: null };
|
|
265
317
|
}
|
|
266
318
|
catch (err) {
|
|
267
319
|
restartInFlight = false;
|
|
268
|
-
return {
|
|
269
|
-
ok: false,
|
|
270
|
-
restarting: false,
|
|
271
|
-
error: err instanceof Error ? err.message : String(err),
|
|
272
|
-
activeTasks: 0,
|
|
273
|
-
};
|
|
320
|
+
return { ok: false, restarting: false, error: err instanceof Error ? err.message : String(err) };
|
|
274
321
|
}
|
|
275
322
|
}
|
|
276
323
|
export function terminateProcessTree(target, opts = {}) {
|
|
@@ -12,7 +12,7 @@ import { validateDingtalkConfig, validateDiscordConfig, validateFeishuConfig, va
|
|
|
12
12
|
import { resolveGuiIntegrationConfig } from '../../agent/mcp/bridge.js';
|
|
13
13
|
import { normalizeWeixinBaseUrl, startWeixinQrLogin, waitForWeixinQrLogin, } from '../../channels/weixin/api.js';
|
|
14
14
|
import { getConfiguredRemoteCdpUrl, getManagedBrowserStatus, launchManagedBrowserSetup, } from '../../browser-profile.js';
|
|
15
|
-
import { requestProcessRestart,
|
|
15
|
+
import { requestProcessRestart, } from '../../core/process-control.js';
|
|
16
16
|
import { getPermissionsStatus, getHostTerminalApp, isValidPermissionKey, requestPermission, } from '../platform.js';
|
|
17
17
|
import { VERSION } from '../../core/version.js';
|
|
18
18
|
import { runtime } from '../runtime.js';
|
|
@@ -389,14 +389,6 @@ app.post('/api/open-preferences', async (c) => {
|
|
|
389
389
|
return c.json(result, result.ok ? 200 : 500);
|
|
390
390
|
});
|
|
391
391
|
app.post('/api/restart', (c) => {
|
|
392
|
-
const activeTasks = getActiveTaskCount();
|
|
393
|
-
if (activeTasks > 0) {
|
|
394
|
-
return c.json({
|
|
395
|
-
ok: false,
|
|
396
|
-
activeTasks,
|
|
397
|
-
error: `${activeTasks} task(s) still running — can't restart. Wait for them to finish or stop them, then retry.`,
|
|
398
|
-
}, 409);
|
|
399
|
-
}
|
|
400
392
|
setTimeout(() => {
|
|
401
393
|
void requestProcessRestart({ log: message => runtime.log(message) });
|
|
402
394
|
}, 50);
|
package/package.json
CHANGED