@delt/claude-alarm 1.4.0 → 1.4.1
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/cli.js +154 -55
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +104 -59
- package/dist/codex/main.js.map +1 -1
- package/dist/hub/server.js +27 -9
- package/dist/hub/server.js.map +1 -1
- package/dist/index.js +27 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/codex/main.js
CHANGED
|
@@ -396,27 +396,48 @@ var defaultSpawn = (command, args) => {
|
|
|
396
396
|
shellSpawns.add(child);
|
|
397
397
|
return child;
|
|
398
398
|
};
|
|
399
|
-
var
|
|
400
|
-
|
|
401
|
-
execFile("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { windowsHide: true, timeout:
|
|
402
|
-
if (err)
|
|
403
|
-
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
try {
|
|
407
|
-
const rows = JSON.parse(stdout);
|
|
408
|
-
if (!Array.isArray(rows) || rows.some((row) => !Number.isInteger(row.pid) || !Number.isInteger(row.parentPid) || typeof row.creationTime !== "string")) {
|
|
409
|
-
throw new Error("invalid process snapshot");
|
|
410
|
-
}
|
|
411
|
-
resolve(rows);
|
|
412
|
-
} catch (error) {
|
|
413
|
-
reject(error);
|
|
414
|
-
}
|
|
399
|
+
var POWERSHELL_TIMEOUT_MS = 5e3;
|
|
400
|
+
var powershell = (script, env) => new Promise((resolve, reject) => {
|
|
401
|
+
execFile("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { windowsHide: true, timeout: POWERSHELL_TIMEOUT_MS, maxBuffer: 4 * 1024 * 1024, env }, (err, stdout) => {
|
|
402
|
+
if (err) reject(err);
|
|
403
|
+
else resolve(stdout);
|
|
415
404
|
});
|
|
416
405
|
});
|
|
417
|
-
var
|
|
418
|
-
|
|
406
|
+
var QUERY_SCRIPT = "@(Get-CimInstance Win32_Process | Where-Object CreationDate | Select-Object @{ n = 'pid'; e = { [int]$_.ProcessId } }, @{ n = 'parentPid'; e = { [int]$_.ParentProcessId } }, @{ n = 'creationTime'; e = { $_.CreationDate.ToUniversalTime().ToString('o') } }) | ConvertTo-Json -Compress";
|
|
407
|
+
var processQuery = (prelude = "") => async () => {
|
|
408
|
+
const rows = JSON.parse(await powershell(prelude + QUERY_SCRIPT));
|
|
409
|
+
if (!Array.isArray(rows) || rows.some((row) => !Number.isInteger(row.pid) || !Number.isInteger(row.parentPid) || typeof row.creationTime !== "string")) {
|
|
410
|
+
throw new Error("invalid process snapshot");
|
|
411
|
+
}
|
|
412
|
+
return rows;
|
|
419
413
|
};
|
|
414
|
+
var queryProcesses = processQuery();
|
|
415
|
+
var END_SCRIPT = [
|
|
416
|
+
"foreach ($t in ($env:CLAUDE_ALARM_END_TARGETS | ConvertFrom-Json)) {",
|
|
417
|
+
" $p = Get-Process -Id $t.pid -ErrorAction SilentlyContinue",
|
|
418
|
+
// Reading Handle opens the process once, and StartTime, Stop-Process and Wait-Process then use that handle; a getter that throws reads as $null.
|
|
419
|
+
' if ($null -eq $p -or $null -eq $p.Handle) { "$($t.pid) is gone or cannot be opened"; continue }',
|
|
420
|
+
" $start = $p.StartTime",
|
|
421
|
+
' if ($null -eq $start) { "$($t.pid) has no readable creation time"; continue }',
|
|
422
|
+
" $start = $start.ToUniversalTime()",
|
|
423
|
+
// Win32_Process creation dates stop at microseconds.
|
|
424
|
+
` if ($start.AddTicks(-($start.Ticks % 10)).ToString('o') -ne $t.creationTime) { "$($t.pid) now belongs to another process"; continue }`,
|
|
425
|
+
' try { Stop-Process -InputObject $p -Force -ErrorAction Stop } catch { "$($t.pid) not ended: $($_.FullyQualifiedErrorId)" }',
|
|
426
|
+
// A refused termination usually means the process is already exiting, so it is waited for all the same.
|
|
427
|
+
' try { Wait-Process -InputObject $p -Timeout 1 -ErrorAction Stop } catch { "$($t.pid) has not exited yet" }',
|
|
428
|
+
"}"
|
|
429
|
+
].join("\n");
|
|
430
|
+
var processEnder = (prelude = "") => async (targets) => {
|
|
431
|
+
await Promise.all(targets.map(async (target) => {
|
|
432
|
+
try {
|
|
433
|
+
const notes = await powershell(prelude + END_SCRIPT, { ...process.env, CLAUDE_ALARM_END_TARGETS: JSON.stringify([target]) });
|
|
434
|
+
for (const note of notes.split(/\r?\n/)) if (note) logger.debug(`codex process ${note}`);
|
|
435
|
+
} catch (err) {
|
|
436
|
+
logger.debug(`ending codex process ${target.pid} failed: ${err.killed ? "timed out" : String(err)}`);
|
|
437
|
+
}
|
|
438
|
+
}));
|
|
439
|
+
};
|
|
440
|
+
var endProcesses = processEnder();
|
|
420
441
|
function descendantsOf(rootPid, rows) {
|
|
421
442
|
const root = rows.find((row) => row.pid === rootPid);
|
|
422
443
|
if (!root) return void 0;
|
|
@@ -438,16 +459,32 @@ function descendantsOf(rootPid, rows) {
|
|
|
438
459
|
return found;
|
|
439
460
|
}
|
|
440
461
|
var exited = (child) => child.exitCode !== null || child.signalCode !== null;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
462
|
+
var SHELL_EXIT_WAIT_MS = 1e3;
|
|
463
|
+
var PROXY_TREE_CLOSE_BUDGET_MS = 2 * POWERSHELL_TIMEOUT_MS + SHELL_EXIT_WAIT_MS;
|
|
464
|
+
var exitOf = (child) => new Promise((resolve) => {
|
|
465
|
+
if (exited(child)) {
|
|
466
|
+
resolve();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const timer = setTimeout(() => {
|
|
470
|
+
logger.debug(`codex proxy shell ${child.pid} has not exited yet`);
|
|
471
|
+
resolve();
|
|
472
|
+
}, SHELL_EXIT_WAIT_MS);
|
|
473
|
+
child.once("exit", () => {
|
|
474
|
+
clearTimeout(timer);
|
|
475
|
+
resolve();
|
|
448
476
|
});
|
|
449
|
-
|
|
450
|
-
|
|
477
|
+
});
|
|
478
|
+
var merged = (before, found) => [...before, ...found.filter((d) => !before.some((b) => b.pid === d.pid && b.creationTime === d.creationTime))];
|
|
479
|
+
function treeKiller(platform = process.platform, end = endProcesses, query = queryProcesses, throughShell = spawnedThroughShell) {
|
|
480
|
+
const recorded = /* @__PURE__ */ new WeakMap();
|
|
481
|
+
const endByIdentity = async (descendants) => {
|
|
482
|
+
if (!descendants.length) return;
|
|
483
|
+
try {
|
|
484
|
+
await end([...descendants].sort((a, b) => b.depth - a.depth).map(({ depth, ...identity }) => identity));
|
|
485
|
+
} catch (err) {
|
|
486
|
+
logger.debug(`ending codex processes failed: ${String(err)}`);
|
|
487
|
+
}
|
|
451
488
|
};
|
|
452
489
|
const snapshot = async () => {
|
|
453
490
|
try {
|
|
@@ -463,20 +500,12 @@ function treeKiller(platform = process.platform, run = runFile, query = queryPro
|
|
|
463
500
|
if (!exited(child)) child.kill();
|
|
464
501
|
return;
|
|
465
502
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
const descendants = await recorded.get(child) ?? [];
|
|
476
|
-
if (!descendants.length) return;
|
|
477
|
-
const current = await snapshot();
|
|
478
|
-
if (!current) return;
|
|
479
|
-
await stopDeepestFirst(descendants.filter((d) => current.some((row) => row.pid === d.pid && row.creationTime === d.creationTime)));
|
|
503
|
+
const [rows, before] = await Promise.all([exited(child) ? void 0 : snapshot(), recorded.get(child)]);
|
|
504
|
+
const found = rows && !exited(child) ? descendantsOf(child.pid, rows) ?? [] : [];
|
|
505
|
+
await endByIdentity(merged(before ?? [], found));
|
|
506
|
+
if (exited(child)) return;
|
|
507
|
+
child.kill();
|
|
508
|
+
await exitOf(child);
|
|
480
509
|
};
|
|
481
510
|
killer.track = async (child) => {
|
|
482
511
|
if (!isShellTree(child) || exited(child)) return;
|
|
@@ -484,8 +513,7 @@ function treeKiller(platform = process.platform, run = runFile, query = queryPro
|
|
|
484
513
|
const latest = (async () => {
|
|
485
514
|
const rows = await snapshot();
|
|
486
515
|
const found = rows && !exited(child) ? descendantsOf(child.pid, rows) ?? [] : [];
|
|
487
|
-
|
|
488
|
-
return [...before, ...found.filter((d) => !before.some((b) => b.pid === d.pid && b.creationTime === d.creationTime))];
|
|
516
|
+
return merged(await earlier ?? [], found);
|
|
489
517
|
})();
|
|
490
518
|
recorded.set(child, latest);
|
|
491
519
|
await latest;
|
|
@@ -1296,6 +1324,31 @@ var CodexAdapter = class {
|
|
|
1296
1324
|
}
|
|
1297
1325
|
};
|
|
1298
1326
|
|
|
1327
|
+
// src/codex/shutdown.ts
|
|
1328
|
+
var ADAPTER_SHUTDOWN_MS = 25e3;
|
|
1329
|
+
function createAdapterShutdown(opts) {
|
|
1330
|
+
let exiting = false;
|
|
1331
|
+
const shutdown2 = () => {
|
|
1332
|
+
if (exiting) return;
|
|
1333
|
+
exiting = true;
|
|
1334
|
+
const timer = setTimeout(() => {
|
|
1335
|
+
logger.warn(`Codex adapter shutdown did not finish within ${ADAPTER_SHUTDOWN_MS}ms; exiting`);
|
|
1336
|
+
opts.exit();
|
|
1337
|
+
}, ADAPTER_SHUTDOWN_MS);
|
|
1338
|
+
const stopped = Promise.resolve().then(opts.stop);
|
|
1339
|
+
const released = Promise.resolve().then(opts.release);
|
|
1340
|
+
void Promise.allSettled([stopped, released]).then(() => {
|
|
1341
|
+
clearTimeout(timer);
|
|
1342
|
+
opts.exit();
|
|
1343
|
+
});
|
|
1344
|
+
};
|
|
1345
|
+
opts.signals.on("SIGINT", shutdown2);
|
|
1346
|
+
opts.signals.on("SIGTERM", shutdown2);
|
|
1347
|
+
opts.stdin?.on("end", shutdown2);
|
|
1348
|
+
opts.stdin?.on("close", shutdown2);
|
|
1349
|
+
return shutdown2;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1299
1352
|
// src/shared/hub-url.ts
|
|
1300
1353
|
import net from "net";
|
|
1301
1354
|
function hubUrlHost(host) {
|
|
@@ -1518,22 +1571,14 @@ var supervised = process.argv.includes("--watch-stdin");
|
|
|
1518
1571
|
var endpoint = controlEndpoint(CONFIG_DIR);
|
|
1519
1572
|
var adapter;
|
|
1520
1573
|
var release;
|
|
1521
|
-
var
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
};
|
|
1530
|
-
process.on("SIGINT", shutdown);
|
|
1531
|
-
process.on("SIGTERM", shutdown);
|
|
1532
|
-
if (supervised) {
|
|
1533
|
-
process.stdin.on("end", shutdown);
|
|
1534
|
-
process.stdin.on("close", shutdown);
|
|
1535
|
-
process.stdin.resume();
|
|
1536
|
-
}
|
|
1574
|
+
var shutdown = createAdapterShutdown({
|
|
1575
|
+
signals: process,
|
|
1576
|
+
stdin: supervised ? process.stdin : void 0,
|
|
1577
|
+
stop: () => adapter?.stop(),
|
|
1578
|
+
release: () => release?.(),
|
|
1579
|
+
exit: () => process.exit(0)
|
|
1580
|
+
});
|
|
1581
|
+
if (supervised) process.stdin.resume();
|
|
1537
1582
|
function send(message, then) {
|
|
1538
1583
|
if (!process.send || !process.connected) {
|
|
1539
1584
|
then?.();
|