@delt/claude-alarm 1.3.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 +288 -60
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +220 -46
- package/dist/codex/main.js.map +1 -1
- package/dist/dashboard/index.html +77 -46
- package/dist/hub/server.js +83 -40
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +83 -40
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/dashboard/index.html +77 -46
package/dist/codex/main.js
CHANGED
|
@@ -357,7 +357,7 @@ var HubClient = class {
|
|
|
357
357
|
};
|
|
358
358
|
|
|
359
359
|
// src/codex/transport.ts
|
|
360
|
-
import { spawn } from "child_process";
|
|
360
|
+
import { execFile, spawn } from "child_process";
|
|
361
361
|
import fs3 from "fs";
|
|
362
362
|
import path3 from "path";
|
|
363
363
|
import { Duplex } from "stream";
|
|
@@ -387,14 +387,144 @@ function findCodex(command, platform = process.platform, env = process.env) {
|
|
|
387
387
|
];
|
|
388
388
|
return candidates.find((file) => !!file && fs3.existsSync(file));
|
|
389
389
|
}
|
|
390
|
+
var shellSpawns = /* @__PURE__ */ new WeakSet();
|
|
391
|
+
var spawnedThroughShell = (child) => shellSpawns.has(child);
|
|
390
392
|
var defaultSpawn = (command, args) => {
|
|
391
393
|
const { file, shell } = resolveCommand(command);
|
|
392
|
-
if (shell) return spawn(
|
|
393
|
-
|
|
394
|
+
if (!shell) return spawn(file, args, { stdio: "pipe", windowsHide: true });
|
|
395
|
+
const child = spawn(`"${file}" ${args.join(" ")}`, { stdio: "pipe", windowsHide: true, shell: true });
|
|
396
|
+
shellSpawns.add(child);
|
|
397
|
+
return child;
|
|
394
398
|
};
|
|
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);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
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;
|
|
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();
|
|
441
|
+
function descendantsOf(rootPid, rows) {
|
|
442
|
+
const root = rows.find((row) => row.pid === rootPid);
|
|
443
|
+
if (!root) return void 0;
|
|
444
|
+
const found = [];
|
|
445
|
+
const seen = /* @__PURE__ */ new Set([root.pid]);
|
|
446
|
+
let level = [root];
|
|
447
|
+
for (let depth = 1; level.length; depth++) {
|
|
448
|
+
const next = [];
|
|
449
|
+
for (const parent of level) {
|
|
450
|
+
for (const row of rows) {
|
|
451
|
+
if (row.parentPid !== parent.pid || seen.has(row.pid) || row.creationTime < parent.creationTime) continue;
|
|
452
|
+
seen.add(row.pid);
|
|
453
|
+
next.push(row);
|
|
454
|
+
found.push({ ...row, depth });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
level = next;
|
|
458
|
+
}
|
|
459
|
+
return found;
|
|
460
|
+
}
|
|
461
|
+
var exited = (child) => child.exitCode !== null || child.signalCode !== null;
|
|
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();
|
|
476
|
+
});
|
|
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
|
+
}
|
|
488
|
+
};
|
|
489
|
+
const snapshot = async () => {
|
|
490
|
+
try {
|
|
491
|
+
return await query();
|
|
492
|
+
} catch (err) {
|
|
493
|
+
logger.debug(`codex process query failed: ${String(err)}`);
|
|
494
|
+
return void 0;
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const isShellTree = (child) => platform === "win32" && child.pid !== void 0 && throughShell(child);
|
|
498
|
+
const killer = async (child) => {
|
|
499
|
+
if (!isShellTree(child)) {
|
|
500
|
+
if (!exited(child)) child.kill();
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
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);
|
|
509
|
+
};
|
|
510
|
+
killer.track = async (child) => {
|
|
511
|
+
if (!isShellTree(child) || exited(child)) return;
|
|
512
|
+
const earlier = recorded.get(child);
|
|
513
|
+
const latest = (async () => {
|
|
514
|
+
const rows = await snapshot();
|
|
515
|
+
const found = rows && !exited(child) ? descendantsOf(child.pid, rows) ?? [] : [];
|
|
516
|
+
return merged(await earlier ?? [], found);
|
|
517
|
+
})();
|
|
518
|
+
recorded.set(child, latest);
|
|
519
|
+
await latest;
|
|
520
|
+
};
|
|
521
|
+
return killer;
|
|
522
|
+
}
|
|
523
|
+
var killTree = treeKiller();
|
|
395
524
|
var HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
396
|
-
function connectProxy(command, spawnFn = defaultSpawn, timeoutMs = HANDSHAKE_TIMEOUT_MS) {
|
|
525
|
+
function connectProxy(command, spawnFn = defaultSpawn, timeoutMs = HANDSHAKE_TIMEOUT_MS, killTreeFn = killTree) {
|
|
397
526
|
const child = spawnFn(command, ["app-server", "proxy"]);
|
|
527
|
+
void killTreeFn.track?.(child);
|
|
398
528
|
child.stdin?.on("error", () => {
|
|
399
529
|
});
|
|
400
530
|
child.stderr?.on("data", (d) => logger.debug(`codex proxy: ${String(d).trim()}`));
|
|
@@ -421,10 +551,12 @@ function connectProxy(command, spawnFn = defaultSpawn, timeoutMs = HANDSHAKE_TIM
|
|
|
421
551
|
const ws = new WebSocket3("ws://localhost/", {
|
|
422
552
|
createConnection: (() => stream)
|
|
423
553
|
});
|
|
424
|
-
|
|
554
|
+
let closing;
|
|
555
|
+
const close = () => closing ??= (async () => {
|
|
425
556
|
ws.terminate();
|
|
426
|
-
child.
|
|
427
|
-
|
|
557
|
+
child.stdin?.end();
|
|
558
|
+
await killTreeFn(child);
|
|
559
|
+
})();
|
|
428
560
|
return new Promise((resolve, reject) => {
|
|
429
561
|
let settled = false;
|
|
430
562
|
const timer = setTimeout(() => fail(new Error(`codex daemon did not answer within ${timeoutMs / 1e3}s`)), timeoutMs);
|
|
@@ -432,8 +564,7 @@ function connectProxy(command, spawnFn = defaultSpawn, timeoutMs = HANDSHAKE_TIM
|
|
|
432
564
|
if (settled) return;
|
|
433
565
|
settled = true;
|
|
434
566
|
clearTimeout(timer);
|
|
435
|
-
close();
|
|
436
|
-
reject(err);
|
|
567
|
+
void close().then(() => reject(err), () => reject(err));
|
|
437
568
|
};
|
|
438
569
|
child.on("error", fail);
|
|
439
570
|
child.once("exit", (code) => fail(new Error(`codex proxy exited (code ${code})`)));
|
|
@@ -444,6 +575,7 @@ function connectProxy(command, spawnFn = defaultSpawn, timeoutMs = HANDSHAKE_TIM
|
|
|
444
575
|
clearTimeout(timer);
|
|
445
576
|
child.on("error", (err) => logger.warn(`codex proxy error: ${err.message}`));
|
|
446
577
|
ws.on("error", (err) => logger.warn(`codex daemon socket error: ${err.message}`));
|
|
578
|
+
void killTreeFn.track?.(child);
|
|
447
579
|
resolve({ ws, close });
|
|
448
580
|
});
|
|
449
581
|
});
|
|
@@ -653,6 +785,8 @@ var CodexAdapter = class {
|
|
|
653
785
|
this.link = new CodexHubLink(opts.hub, { id: randomUUID2(), host: this.hostName }, (call) => this.onCall(call), opts.linkReconnectMs);
|
|
654
786
|
}
|
|
655
787
|
conn;
|
|
788
|
+
connecting;
|
|
789
|
+
closing = /* @__PURE__ */ new Set();
|
|
656
790
|
rpc;
|
|
657
791
|
threads = /* @__PURE__ */ new Map();
|
|
658
792
|
approvals = /* @__PURE__ */ new Map();
|
|
@@ -668,22 +802,29 @@ var CodexAdapter = class {
|
|
|
668
802
|
start() {
|
|
669
803
|
this.stopped = false;
|
|
670
804
|
this.link.connect();
|
|
671
|
-
|
|
805
|
+
this.connecting = this.connect();
|
|
672
806
|
}
|
|
673
|
-
stop() {
|
|
807
|
+
async stop() {
|
|
674
808
|
this.stopped = true;
|
|
675
809
|
this.link.disconnect();
|
|
676
810
|
if (this.retryTimer) clearTimeout(this.retryTimer);
|
|
677
811
|
this.retryTimer = void 0;
|
|
678
812
|
for (const id of [...this.threads.keys()]) this.drop(id);
|
|
679
|
-
this.conn
|
|
813
|
+
this.closeConn(this.conn);
|
|
814
|
+
await this.connecting;
|
|
815
|
+
await Promise.all(this.closing);
|
|
816
|
+
}
|
|
817
|
+
closeConn(conn) {
|
|
818
|
+
if (!conn) return;
|
|
819
|
+
const closing = conn.close().finally(() => this.closing.delete(closing));
|
|
820
|
+
this.closing.add(closing);
|
|
680
821
|
}
|
|
681
822
|
async connect() {
|
|
682
823
|
let rpc;
|
|
683
824
|
try {
|
|
684
825
|
const conn = await connectProxy(this.opts.command, this.opts.spawnFn);
|
|
685
826
|
if (this.stopped) {
|
|
686
|
-
|
|
827
|
+
this.closeConn(conn);
|
|
687
828
|
return;
|
|
688
829
|
}
|
|
689
830
|
const live = new RpcClient(conn.ws, this.opts.rpcTimeoutMs);
|
|
@@ -715,7 +856,7 @@ var CodexAdapter = class {
|
|
|
715
856
|
});
|
|
716
857
|
if (rpc && this.rpc === rpc) {
|
|
717
858
|
this.rpc = void 0;
|
|
718
|
-
this.conn
|
|
859
|
+
this.closeConn(this.conn);
|
|
719
860
|
this.conn = void 0;
|
|
720
861
|
}
|
|
721
862
|
for (const id of [...this.threads.keys()]) this.drop(id);
|
|
@@ -725,7 +866,7 @@ var CodexAdapter = class {
|
|
|
725
866
|
onDaemonLost() {
|
|
726
867
|
this.setReady(false);
|
|
727
868
|
this.rpc = void 0;
|
|
728
|
-
this.conn
|
|
869
|
+
this.closeConn(this.conn);
|
|
729
870
|
this.conn = void 0;
|
|
730
871
|
for (const id of [...this.threads.keys()]) this.drop(id);
|
|
731
872
|
if (!this.stopped) logger.warn("Codex daemon connection lost");
|
|
@@ -735,7 +876,7 @@ var CodexAdapter = class {
|
|
|
735
876
|
if (this.stopped || this.retryTimer) return;
|
|
736
877
|
this.retryTimer = setTimeout(() => {
|
|
737
878
|
this.retryTimer = void 0;
|
|
738
|
-
|
|
879
|
+
this.connecting = this.connect();
|
|
739
880
|
}, this.delay);
|
|
740
881
|
this.delay = Math.min(this.delay * 2, this.opts.reconnectMaxMs ?? 6e4);
|
|
741
882
|
}
|
|
@@ -1028,9 +1169,9 @@ var CodexAdapter = class {
|
|
|
1028
1169
|
}
|
|
1029
1170
|
onHubMessage(threadId, msg) {
|
|
1030
1171
|
if (msg.type === "message_to_session") {
|
|
1031
|
-
this.enqueue(threadId, async () => textInput(msg.content, msg.source));
|
|
1172
|
+
this.enqueue(threadId, async () => textInput(msg.content, msg.source), msg.source);
|
|
1032
1173
|
} else if (msg.type === "image_to_session") {
|
|
1033
|
-
this.enqueue(threadId, () => imageInput(msg.imagePath, msg.mimeType, msg.content, msg.source));
|
|
1174
|
+
this.enqueue(threadId, () => imageInput(msg.imagePath, msg.mimeType, msg.content, msg.source), msg.source);
|
|
1034
1175
|
} else if (msg.type === "permission_response") {
|
|
1035
1176
|
this.answer(threadId, msg.requestId, msg.choiceId);
|
|
1036
1177
|
} else if (msg.type === "codex_close") {
|
|
@@ -1038,16 +1179,16 @@ var CodexAdapter = class {
|
|
|
1038
1179
|
}
|
|
1039
1180
|
}
|
|
1040
1181
|
// One message at a time per conversation, so a message right behind another sees the turn the first one started.
|
|
1041
|
-
enqueue(threadId, build) {
|
|
1182
|
+
enqueue(threadId, build, source) {
|
|
1042
1183
|
const t = this.threads.get(threadId);
|
|
1043
1184
|
if (!t) return;
|
|
1044
|
-
t.sending = t.sending.then(() => this.deliver(t, build)).catch((err) => logger.warn(`Codex delivery for ${threadId} failed: ${err.message}`));
|
|
1185
|
+
t.sending = t.sending.then(() => this.deliver(t, build, source)).catch((err) => logger.warn(`Codex delivery for ${threadId} failed: ${err.message}`));
|
|
1045
1186
|
}
|
|
1046
|
-
async deliver(t, build) {
|
|
1187
|
+
async deliver(t, build, source) {
|
|
1047
1188
|
const threadId = t.thread.id;
|
|
1048
1189
|
if (this.threads.get(threadId) !== t) return;
|
|
1049
|
-
if (
|
|
1050
|
-
this.
|
|
1190
|
+
if (this.waitingForAnswer(t)) {
|
|
1191
|
+
this.refuseWhileWaiting(threadId);
|
|
1051
1192
|
return;
|
|
1052
1193
|
}
|
|
1053
1194
|
let input;
|
|
@@ -1068,20 +1209,37 @@ var CodexAdapter = class {
|
|
|
1068
1209
|
const unsubscribed = !t.subscribed;
|
|
1069
1210
|
const running = await this.runningTurn(rpc, threadId);
|
|
1070
1211
|
if (this.threads.get(threadId) !== t) return;
|
|
1212
|
+
if (this.waitingForAnswer(t)) {
|
|
1213
|
+
this.abandonDelivery(t, wasUnrelayed);
|
|
1214
|
+
this.refuseWhileWaiting(threadId);
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1071
1217
|
if (unsubscribed) t.unrelayed = true;
|
|
1072
1218
|
if (running) {
|
|
1073
1219
|
await rpc.request("turn/steer", { threadId, expectedTurnId: running, input });
|
|
1074
|
-
this.notify(threadId, "Queued", "Queued: Codex will read it after its current step.", "info");
|
|
1220
|
+
this.notify(threadId, "Queued", "Queued: Codex will read it after its current step.", "info", source);
|
|
1075
1221
|
return;
|
|
1076
1222
|
}
|
|
1077
1223
|
await rpc.request("turn/start", { threadId, input });
|
|
1078
1224
|
} catch (err) {
|
|
1079
|
-
t
|
|
1080
|
-
t.unrelayed = wasUnrelayed;
|
|
1081
|
-
this.releaseLater(t);
|
|
1225
|
+
this.abandonDelivery(t, wasUnrelayed);
|
|
1082
1226
|
this.notify(threadId, "Not delivered", `Codex rejected the message: ${err.message}`, "warning");
|
|
1083
1227
|
}
|
|
1084
1228
|
}
|
|
1229
|
+
abandonDelivery(t, wasUnrelayed) {
|
|
1230
|
+
t.pendingTurn = false;
|
|
1231
|
+
t.unrelayed = wasUnrelayed;
|
|
1232
|
+
this.releaseLater(t);
|
|
1233
|
+
}
|
|
1234
|
+
// The approval request itself can arrive before the status broadcast that marks the thread as waiting.
|
|
1235
|
+
waitingForAnswer(t) {
|
|
1236
|
+
if (hubStatus(t.thread.status) === "waiting_input") return true;
|
|
1237
|
+
for (const a of this.approvals.values()) if (a.threadId === t.thread.id && !a.answered) return true;
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
refuseWhileWaiting(threadId) {
|
|
1241
|
+
this.notify(threadId, "Not delivered", "Codex is waiting for an approval or input. Answer it first, then send the message again.", "warning");
|
|
1242
|
+
}
|
|
1085
1243
|
// The list is unavailable before a new conversation's first turn; starting a turn is safe then, since turn/start steers a running turn.
|
|
1086
1244
|
async runningTurn(rpc, threadId) {
|
|
1087
1245
|
try {
|
|
@@ -1161,11 +1319,36 @@ var CodexAdapter = class {
|
|
|
1161
1319
|
this.approvals.delete(requestId);
|
|
1162
1320
|
this.threads.get(a.threadId)?.hub.send({ type: "permission_resolved", sessionId: codexSessionId(a.threadId), requestId, state });
|
|
1163
1321
|
}
|
|
1164
|
-
notify(threadId, title, message, level) {
|
|
1165
|
-
this.threads.get(threadId)?.hub.send({ type: "notify", sessionId: codexSessionId(threadId), title, message, level });
|
|
1322
|
+
notify(threadId, title, message, level, to) {
|
|
1323
|
+
this.threads.get(threadId)?.hub.send({ type: "notify", sessionId: codexSessionId(threadId), title, message, level, ...to ? { to } : {} });
|
|
1166
1324
|
}
|
|
1167
1325
|
};
|
|
1168
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
|
+
|
|
1169
1352
|
// src/shared/hub-url.ts
|
|
1170
1353
|
import net from "net";
|
|
1171
1354
|
function hubUrlHost(host) {
|
|
@@ -1388,23 +1571,14 @@ var supervised = process.argv.includes("--watch-stdin");
|
|
|
1388
1571
|
var endpoint = controlEndpoint(CONFIG_DIR);
|
|
1389
1572
|
var adapter;
|
|
1390
1573
|
var release;
|
|
1391
|
-
var
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
else exit();
|
|
1400
|
-
};
|
|
1401
|
-
process.on("SIGINT", shutdown);
|
|
1402
|
-
process.on("SIGTERM", shutdown);
|
|
1403
|
-
if (supervised) {
|
|
1404
|
-
process.stdin.on("end", shutdown);
|
|
1405
|
-
process.stdin.on("close", shutdown);
|
|
1406
|
-
process.stdin.resume();
|
|
1407
|
-
}
|
|
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();
|
|
1408
1582
|
function send(message, then) {
|
|
1409
1583
|
if (!process.send || !process.connected) {
|
|
1410
1584
|
then?.();
|