muuuuse 3.3.4 → 3.3.6
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/package.json +1 -1
- package/src/agents.js +88 -30
- package/src/runtime.js +191 -4
package/package.json
CHANGED
package/src/agents.js
CHANGED
|
@@ -179,22 +179,73 @@ function readCodexSeatClaim(sessionId) {
|
|
|
179
179
|
return null;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
const
|
|
182
|
+
const claims = readCodexSeatClaims(sessionId);
|
|
183
|
+
return claims[0] || null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function readCodexSeatClaims(sessionId) {
|
|
187
|
+
if (!sessionId) {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const candidatePaths = listCodexSnapshotPaths(sessionId);
|
|
192
|
+
return candidatePaths
|
|
193
|
+
.map((snapshotPath) => {
|
|
194
|
+
let text;
|
|
195
|
+
try {
|
|
196
|
+
text = fs.readFileSync(snapshotPath, "utf8");
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const seatMatch = text.match(/declare -x MUUUUSE_SEAT="([^"]+)"/);
|
|
202
|
+
const sessionMatch = text.match(/declare -x MUUUUSE_SESSION="([^"]+)"/);
|
|
203
|
+
if (!seatMatch || !sessionMatch) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let mtimeMs = 0;
|
|
208
|
+
try {
|
|
209
|
+
mtimeMs = fs.statSync(snapshotPath).mtimeMs;
|
|
210
|
+
} catch {
|
|
211
|
+
// Best effort only.
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
seatId: seatMatch[1],
|
|
216
|
+
sessionName: sessionMatch[1],
|
|
217
|
+
snapshotPath,
|
|
218
|
+
mtimeMs,
|
|
219
|
+
};
|
|
220
|
+
})
|
|
221
|
+
.filter((claim) => claim !== null)
|
|
222
|
+
.sort((left, right) => right.mtimeMs - left.mtimeMs || left.snapshotPath.localeCompare(right.snapshotPath));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function listCodexSnapshotPaths(sessionId) {
|
|
226
|
+
const exactName = `${sessionId}.sh`;
|
|
227
|
+
const prefix = `${sessionId}.`;
|
|
228
|
+
const candidateNames = [];
|
|
229
|
+
|
|
183
230
|
try {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
231
|
+
for (const entry of fs.readdirSync(CODEX_SNAPSHOT_ROOT, { withFileTypes: true })) {
|
|
232
|
+
if (!entry.isFile()) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
190
235
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
236
|
+
if (entry.name === exactName || (entry.name.startsWith(prefix) && entry.name.endsWith(".sh"))) {
|
|
237
|
+
candidateNames.push(entry.name);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
195
240
|
} catch {
|
|
196
|
-
|
|
241
|
+
// Snapshot directory may not exist yet.
|
|
197
242
|
}
|
|
243
|
+
|
|
244
|
+
return uniquePaths(candidateNames.map((name) => path.join(CODEX_SNAPSHOT_ROOT, name)));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function candidateHasClaim(candidate, predicate) {
|
|
248
|
+
return readCodexSeatClaims(candidate.sessionId).some((claim) => predicate(claim));
|
|
198
249
|
}
|
|
199
250
|
|
|
200
251
|
function selectClaimedCodexCandidatePath(candidates, options = {}) {
|
|
@@ -204,29 +255,28 @@ function selectClaimedCodexCandidatePath(candidates, options = {}) {
|
|
|
204
255
|
return null;
|
|
205
256
|
}
|
|
206
257
|
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const exactMatches = annotated.filter((candidate) => (
|
|
213
|
-
candidate.claim?.seatId === seatId &&
|
|
214
|
-
candidate.claim?.sessionName === sessionName
|
|
258
|
+
const exactMatches = candidates.filter((candidate) => (
|
|
259
|
+
candidateHasClaim(candidate, (claim) => (
|
|
260
|
+
claim.seatId === seatId &&
|
|
261
|
+
claim.sessionName === sessionName
|
|
262
|
+
))
|
|
215
263
|
));
|
|
216
264
|
if (exactMatches.length === 1) {
|
|
217
265
|
return exactMatches[0].path;
|
|
218
266
|
}
|
|
219
267
|
|
|
220
|
-
const otherSeatClaims =
|
|
221
|
-
candidate
|
|
222
|
-
|
|
268
|
+
const otherSeatClaims = candidates.filter((candidate) => (
|
|
269
|
+
candidateHasClaim(candidate, (claim) => (
|
|
270
|
+
claim.sessionName === sessionName &&
|
|
271
|
+
claim.seatId !== seatId
|
|
272
|
+
))
|
|
223
273
|
));
|
|
224
274
|
if (otherSeatClaims.length === 0) {
|
|
225
275
|
return null;
|
|
226
276
|
}
|
|
227
277
|
|
|
228
278
|
const foreignPaths = new Set(otherSeatClaims.map((candidate) => candidate.path));
|
|
229
|
-
const remaining =
|
|
279
|
+
const remaining = candidates.filter((candidate) => !foreignPaths.has(candidate.path));
|
|
230
280
|
if (remaining.length === 1) {
|
|
231
281
|
return remaining[0].path;
|
|
232
282
|
}
|
|
@@ -332,8 +382,10 @@ function selectExactClaimedCodexCandidate(candidates, options = {}, processStart
|
|
|
332
382
|
|
|
333
383
|
const exactMatches = rankCodexCandidates(
|
|
334
384
|
candidates.filter((candidate) => {
|
|
335
|
-
|
|
336
|
-
|
|
385
|
+
return candidateHasClaim(candidate, (claim) => (
|
|
386
|
+
claim.seatId === seatId &&
|
|
387
|
+
claim.sessionName === sessionName
|
|
388
|
+
));
|
|
337
389
|
}),
|
|
338
390
|
processStartedAtMs
|
|
339
391
|
);
|
|
@@ -349,8 +401,11 @@ function filterForeignClaimedCodexCandidates(candidates, options = {}) {
|
|
|
349
401
|
}
|
|
350
402
|
|
|
351
403
|
return candidates.filter((candidate) => {
|
|
352
|
-
|
|
353
|
-
|
|
404
|
+
return !candidateHasClaim(candidate, (claim) => (
|
|
405
|
+
claim.sessionName === sessionName &&
|
|
406
|
+
claim.seatId &&
|
|
407
|
+
claim.seatId !== seatId
|
|
408
|
+
));
|
|
354
409
|
});
|
|
355
410
|
}
|
|
356
411
|
|
|
@@ -391,8 +446,11 @@ function selectCodexCandidatePath(candidates, currentPath, processStartedAtMs, o
|
|
|
391
446
|
seatId &&
|
|
392
447
|
sessionName &&
|
|
393
448
|
cwdMatches.some((candidate) => {
|
|
394
|
-
|
|
395
|
-
|
|
449
|
+
return candidateHasClaim(candidate, (claim) => (
|
|
450
|
+
claim.sessionName === sessionName &&
|
|
451
|
+
claim.seatId &&
|
|
452
|
+
claim.seatId !== seatId
|
|
453
|
+
));
|
|
396
454
|
})
|
|
397
455
|
);
|
|
398
456
|
const allowedMatches = filterForeignClaimedCodexCandidates(cwdMatches, options);
|
package/src/runtime.js
CHANGED
|
@@ -42,6 +42,7 @@ const {
|
|
|
42
42
|
// A short settle delay keeps interactive CLIs from treating submit as another newline.
|
|
43
43
|
const TYPE_CHUNK_DELAY_MS = 45;
|
|
44
44
|
const TYPE_CHUNK_SIZE = 24;
|
|
45
|
+
const GEMINI_PASTE_SETTLE_MS = 200;
|
|
45
46
|
const BRACKETED_PASTE_START = "\u001b[200~";
|
|
46
47
|
const BRACKETED_PASTE_END = "\u001b[201~";
|
|
47
48
|
const GEMINI_SHARED_ENTRY_NAMES = new Set([
|
|
@@ -56,8 +57,11 @@ const EMITTED_ANSWER_TTL_MS = 5 * 60 * 1000;
|
|
|
56
57
|
const MAX_RECENT_INBOUND_RELAYS = 12;
|
|
57
58
|
const MAX_RECENT_EMITTED_ANSWERS = 48;
|
|
58
59
|
const STOP_FORCE_KILL_MS = 1200;
|
|
60
|
+
const STOP_PURGE_WAIT_MS = STOP_FORCE_KILL_MS + 1200;
|
|
61
|
+
const STOP_PURGE_POLL_MS = 60;
|
|
59
62
|
const SEAT_JOIN_WAIT_MS = 3000;
|
|
60
63
|
const SEAT_JOIN_POLL_MS = 60;
|
|
64
|
+
const MAX_PENDING_PASSIVE_INPUT_CHARS = 512;
|
|
61
65
|
const CHILD_ENV_DROP_KEYS = [
|
|
62
66
|
"CODEX_CI",
|
|
63
67
|
"CODEX_MANAGED_BY_NPM",
|
|
@@ -200,6 +204,7 @@ function ensureSeatGeminiCliHome(homeDir, cwd, seatId, baseEnv = process.env) {
|
|
|
200
204
|
const sourceHomeRoot = String(baseEnv.GEMINI_CLI_HOME || homeDir).trim() || homeDir;
|
|
201
205
|
const sourceGeminiDir = path.join(sourceHomeRoot, ".gemini");
|
|
202
206
|
const targetHomeRoot = getSeatGeminiCliHome(homeDir, cwd, seatId);
|
|
207
|
+
fs.rmSync(targetHomeRoot, { recursive: true, force: true });
|
|
203
208
|
const targetGeminiDir = ensureDir(path.join(targetHomeRoot, ".gemini"));
|
|
204
209
|
|
|
205
210
|
let sourceEntries = [];
|
|
@@ -653,6 +658,103 @@ function isMeaningfulTerminalInput(input) {
|
|
|
653
658
|
return stripPassiveTerminalInput(input).length > 0;
|
|
654
659
|
}
|
|
655
660
|
|
|
661
|
+
function consumeTerminalProxyInput(input, pendingPassiveInput = "") {
|
|
662
|
+
const combined = `${pendingPassiveInput}${String(input || "")}`;
|
|
663
|
+
let forwardText = "";
|
|
664
|
+
let index = 0;
|
|
665
|
+
|
|
666
|
+
while (index < combined.length) {
|
|
667
|
+
const current = combined[index];
|
|
668
|
+
if (current !== "\u001b") {
|
|
669
|
+
forwardText += current;
|
|
670
|
+
index += 1;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (index + 1 >= combined.length) {
|
|
675
|
+
forwardText += current;
|
|
676
|
+
index += 1;
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const next = combined[index + 1];
|
|
681
|
+
if (next === "]") {
|
|
682
|
+
const belIndex = combined.indexOf("\u0007", index + 2);
|
|
683
|
+
const stIndex = combined.indexOf("\u001b\\", index + 2);
|
|
684
|
+
const endIndex = (
|
|
685
|
+
belIndex !== -1 && stIndex !== -1 ? Math.min(belIndex, stIndex) :
|
|
686
|
+
belIndex !== -1 ? belIndex :
|
|
687
|
+
stIndex
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
if (endIndex === -1) {
|
|
691
|
+
const pending = combined.slice(index).slice(0, MAX_PENDING_PASSIVE_INPUT_CHARS);
|
|
692
|
+
return {
|
|
693
|
+
forwardText,
|
|
694
|
+
meaningful: isMeaningfulTerminalInput(forwardText),
|
|
695
|
+
pendingPassiveInput: pending,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
index = endIndex + (endIndex === stIndex ? 2 : 1);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (next === "[") {
|
|
704
|
+
if (index + 2 >= combined.length) {
|
|
705
|
+
return {
|
|
706
|
+
forwardText,
|
|
707
|
+
meaningful: isMeaningfulTerminalInput(forwardText),
|
|
708
|
+
pendingPassiveInput: combined.slice(index).slice(0, MAX_PENDING_PASSIVE_INPUT_CHARS),
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
let endIndex = index + 2;
|
|
713
|
+
while (endIndex < combined.length && !/[@-~]/.test(combined[endIndex])) {
|
|
714
|
+
endIndex += 1;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (endIndex >= combined.length) {
|
|
718
|
+
const pending = combined.slice(index);
|
|
719
|
+
if (/^\u001b\[(?:\??[0-9;]*)?$/.test(pending)) {
|
|
720
|
+
return {
|
|
721
|
+
forwardText,
|
|
722
|
+
meaningful: isMeaningfulTerminalInput(forwardText),
|
|
723
|
+
pendingPassiveInput: pending.slice(0, MAX_PENDING_PASSIVE_INPUT_CHARS),
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
forwardText += pending;
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
const sequence = combined.slice(index, endIndex + 1);
|
|
732
|
+
if (
|
|
733
|
+
sequence === "\u001b[I" ||
|
|
734
|
+
sequence === "\u001b[O" ||
|
|
735
|
+
/^\u001b\[\d+;\d+R$/.test(sequence) ||
|
|
736
|
+
/^\u001b\[\?[0-9;]*c$/.test(sequence)
|
|
737
|
+
) {
|
|
738
|
+
index = endIndex + 1;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
forwardText += sequence;
|
|
743
|
+
index = endIndex + 1;
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
forwardText += current;
|
|
748
|
+
index += 1;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return {
|
|
752
|
+
forwardText,
|
|
753
|
+
meaningful: isMeaningfulTerminalInput(forwardText),
|
|
754
|
+
pendingPassiveInput: "",
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
|
|
656
758
|
async function sendTextAndEnter(child, text, shouldAbort = () => false) {
|
|
657
759
|
const options = typeof shouldAbort === "function" ? { shouldAbort } : (shouldAbort || {});
|
|
658
760
|
const shouldStop = typeof options.shouldAbort === "function" ? options.shouldAbort : () => false;
|
|
@@ -660,7 +762,7 @@ async function sendTextAndEnter(child, text, shouldAbort = () => false) {
|
|
|
660
762
|
const payload = normalizeRelayPayloadForTyping(text);
|
|
661
763
|
|
|
662
764
|
if (payload.length > 0) {
|
|
663
|
-
if (agentType === "codex") {
|
|
765
|
+
if (agentType === "codex" || agentType === "gemini") {
|
|
664
766
|
if (shouldStop() || !child) {
|
|
665
767
|
return false;
|
|
666
768
|
}
|
|
@@ -670,6 +772,10 @@ async function sendTextAndEnter(child, text, shouldAbort = () => false) {
|
|
|
670
772
|
} catch {
|
|
671
773
|
return false;
|
|
672
774
|
}
|
|
775
|
+
|
|
776
|
+
if (agentType === "gemini") {
|
|
777
|
+
await sleep(GEMINI_PASTE_SETTLE_MS);
|
|
778
|
+
}
|
|
673
779
|
} else {
|
|
674
780
|
for (const chunk of chunkRelayPayloadForTyping(payload)) {
|
|
675
781
|
if (shouldStop() || !child) {
|
|
@@ -730,6 +836,7 @@ class ArmedSeat {
|
|
|
730
836
|
this.forceKillTimer = null;
|
|
731
837
|
this.identity = loadOrCreateSeatIdentity(this.paths);
|
|
732
838
|
this.lastUserInputAtMs = 0;
|
|
839
|
+
this.pendingPassiveInput = "";
|
|
733
840
|
this.pendingInboundContext = null;
|
|
734
841
|
this.recentInboundRelays = [];
|
|
735
842
|
this.recentEmittedAnswers = [];
|
|
@@ -843,14 +950,16 @@ class ArmedSeat {
|
|
|
843
950
|
installStdinProxy() {
|
|
844
951
|
const handleData = (chunk) => {
|
|
845
952
|
const chunkText = chunk.toString("utf8");
|
|
846
|
-
|
|
953
|
+
const proxyInput = consumeTerminalProxyInput(chunkText, this.pendingPassiveInput);
|
|
954
|
+
this.pendingPassiveInput = proxyInput.pendingPassiveInput;
|
|
955
|
+
if (proxyInput.meaningful) {
|
|
847
956
|
this.lastUserInputAtMs = Date.now();
|
|
848
957
|
this.pendingInboundContext = null;
|
|
849
958
|
}
|
|
850
|
-
if (!this.child) {
|
|
959
|
+
if (!this.child || proxyInput.forwardText.length === 0) {
|
|
851
960
|
return;
|
|
852
961
|
}
|
|
853
|
-
this.child.write(
|
|
962
|
+
this.child.write(proxyInput.forwardText);
|
|
854
963
|
};
|
|
855
964
|
|
|
856
965
|
const handleEnd = () => {
|
|
@@ -1537,6 +1646,8 @@ class ArmedSeat {
|
|
|
1537
1646
|
exitedAt: new Date().toISOString(),
|
|
1538
1647
|
state: "exited",
|
|
1539
1648
|
});
|
|
1649
|
+
|
|
1650
|
+
purgeSeatTransientState(this.sessionPaths.dir, this.paths.dir, this.cwd, this.seatId);
|
|
1540
1651
|
}
|
|
1541
1652
|
}
|
|
1542
1653
|
|
|
@@ -1661,6 +1772,10 @@ function stopAllSessions() {
|
|
|
1661
1772
|
}
|
|
1662
1773
|
}
|
|
1663
1774
|
|
|
1775
|
+
for (const session of report.sessions) {
|
|
1776
|
+
finalizeStoppedSession(session);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1664
1779
|
return {
|
|
1665
1780
|
requestedAt,
|
|
1666
1781
|
sessions: report.sessions,
|
|
@@ -1670,6 +1785,7 @@ function stopAllSessions() {
|
|
|
1670
1785
|
module.exports = {
|
|
1671
1786
|
ArmedSeat,
|
|
1672
1787
|
buildChildEnv,
|
|
1788
|
+
consumeTerminalProxyInput,
|
|
1673
1789
|
ensureSeatGeminiCliHome,
|
|
1674
1790
|
chunkRelayPayloadForTyping,
|
|
1675
1791
|
getStatusReport,
|
|
@@ -1694,6 +1810,77 @@ function signalPid(pid, signal) {
|
|
|
1694
1810
|
}
|
|
1695
1811
|
}
|
|
1696
1812
|
|
|
1813
|
+
function finalizeStoppedSession(session, timeoutMs = STOP_PURGE_WAIT_MS) {
|
|
1814
|
+
if (!session || !Array.isArray(session.seats) || session.seats.length === 0) {
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
const deadline = Date.now() + timeoutMs;
|
|
1819
|
+
while (Date.now() <= deadline) {
|
|
1820
|
+
const pendingSeats = session.seats.filter((seat) => isPidAlive(seat.wrapperPid) || isPidAlive(seat.childPid));
|
|
1821
|
+
if (pendingSeats.length === 0) {
|
|
1822
|
+
break;
|
|
1823
|
+
}
|
|
1824
|
+
sleepSync(STOP_PURGE_POLL_MS);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
for (const seat of session.seats) {
|
|
1828
|
+
if (isPidAlive(seat.childPid)) {
|
|
1829
|
+
signalProcessFamily(seat.childPid, "SIGKILL");
|
|
1830
|
+
}
|
|
1831
|
+
if (isPidAlive(seat.wrapperPid)) {
|
|
1832
|
+
signalPid(seat.wrapperPid, "SIGKILL");
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
sleepSync(STOP_PURGE_POLL_MS);
|
|
1837
|
+
|
|
1838
|
+
const sessionDir = getSessionPaths(session.sessionName).dir;
|
|
1839
|
+
for (const seat of session.seats) {
|
|
1840
|
+
purgeSeatTransientState(sessionDir, getSeatPaths(session.sessionName, seat.seatId).dir, seat.cwd, seat.seatId);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
function purgeSeatTransientState(sessionDir, seatDir, cwd, seatId) {
|
|
1845
|
+
let geminiSessionDir = null;
|
|
1846
|
+
try {
|
|
1847
|
+
fs.rmSync(seatDir, { recursive: true, force: true });
|
|
1848
|
+
} catch {
|
|
1849
|
+
// Best effort only.
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
try {
|
|
1853
|
+
const homeDir = String(process.env.HOME || "").trim() || "/root";
|
|
1854
|
+
const geminiSeatHome = getSeatGeminiCliHome(homeDir, cwd, seatId);
|
|
1855
|
+
geminiSessionDir = path.dirname(geminiSeatHome);
|
|
1856
|
+
fs.rmSync(geminiSeatHome, { recursive: true, force: true });
|
|
1857
|
+
} catch {
|
|
1858
|
+
// Best effort only.
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
if (geminiSessionDir) {
|
|
1862
|
+
try {
|
|
1863
|
+
const remainingGeminiSeatDirs = fs.readdirSync(geminiSessionDir, { withFileTypes: true })
|
|
1864
|
+
.filter((entry) => entry.isDirectory() && /^seat-\d+$/.test(entry.name));
|
|
1865
|
+
if (remainingGeminiSeatDirs.length === 0) {
|
|
1866
|
+
fs.rmSync(geminiSessionDir, { recursive: true, force: true });
|
|
1867
|
+
}
|
|
1868
|
+
} catch {
|
|
1869
|
+
// Best effort only.
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
try {
|
|
1874
|
+
const remainingSeatDirs = fs.readdirSync(sessionDir, { withFileTypes: true })
|
|
1875
|
+
.filter((entry) => entry.isDirectory() && /^seat-\d+$/.test(entry.name));
|
|
1876
|
+
if (remainingSeatDirs.length === 0) {
|
|
1877
|
+
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
1878
|
+
}
|
|
1879
|
+
} catch {
|
|
1880
|
+
// Best effort only.
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1697
1884
|
function signalProcessTree(rootPid, signal) {
|
|
1698
1885
|
const descendants = getChildProcesses(rootPid);
|
|
1699
1886
|
let delivered = 0;
|