blun-king-cli 9.1.589 → 9.1.595

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 (46) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +12 -0
  3. package/agent-spine-plugin/CHANGELOG.md +1 -0
  4. package/agent-spine-plugin/docs/host-integration.md +15 -1
  5. package/agent-spine-plugin/docs/preflight-recall.md +1 -1
  6. package/agent-spine-plugin/scripts/check-install-hook.js +6 -5
  7. package/agent-spine-plugin/scripts/check-install-king.js +37 -0
  8. package/agent-spine-plugin/scripts/check-install.js +6 -1
  9. package/agent-spine-plugin/scripts/release-check.js +3 -2
  10. package/agent-spine-plugin/src/cli-agent.js +20 -1
  11. package/agent-spine-plugin/src/cli.js +1 -0
  12. package/agent-spine-plugin/src/index.js +1 -1
  13. package/agent-spine-plugin/src/lib/delivery-command-actions.js +16 -7
  14. package/agent-spine-plugin/src/lib/gateway-control.js +69 -1
  15. package/agent-spine-plugin/src/lib/gateway-host-fencing.js +92 -0
  16. package/agent-spine-plugin/src/lib/gateway-host-lifecycle.js +9 -1
  17. package/agent-spine-plugin/src/lib/gateway-prepared-host.js +28 -0
  18. package/agent-spine-plugin/src/lib/gateway-runs.js +46 -10
  19. package/agent-spine-plugin/src/lib/gateway-runtime.js +1 -1
  20. package/agent-spine-plugin/src/lib/gateway-state.js +3 -1
  21. package/agent-spine-plugin/src/lib/hook-context.js +8 -3
  22. package/agent-spine-plugin/src/lib/hook-output.js +2 -3
  23. package/agent-spine-plugin/src/lib/hook-process-advisory.js +1 -2
  24. package/agent-spine-plugin/src/lib/host-instruction-budget.js +17 -0
  25. package/agent-spine-plugin/src/lib/preflight.js +5 -19
  26. package/agent-spine-plugin/src/lib/source-roots.js +3 -2
  27. package/agent-spine-plugin/src/worker.js +30 -12
  28. package/bin/agentspine-king-goal-inbox.mjs +127 -0
  29. package/bin/agentspine-king-goal-intake.mjs +106 -0
  30. package/bin/agentspine-king-host-runner.mjs +109 -0
  31. package/bin/agentspine-king-snapshot-policy.cjs +80 -0
  32. package/bin/agentspine-king-status-policy.mjs +226 -0
  33. package/bin/agentspine-king-worker-host.mjs +160 -0
  34. package/bin/core-bootstrap.js +20 -3
  35. package/bin/launcher-mode.js +38 -1
  36. package/bin/launcher-restart-policy.cjs +131 -0
  37. package/bin/launcher-runtime.js +48 -9
  38. package/bin/runtime-exit-ledger.cjs +144 -0
  39. package/bin/runtime-exit-ledger.d.cts +23 -0
  40. package/bin/windows-node-crash-dump.cjs +289 -0
  41. package/blun.mjs +83241 -74234
  42. package/bundled-agent-sources.json +109 -34
  43. package/codebase-index/codebase_index.py +470 -0
  44. package/package.json +6 -1
  45. package/telegram-plugin/dist/bridge.mjs +390 -4
  46. package/worker-host.mjs +348023 -0
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
+ import { appendFileSync, chmodSync, closeSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
5
5
  import { homedir } from "node:os";
@@ -59522,6 +59522,380 @@ function isMentioned(ctx, botUsername, extraPatterns) {
59522
59522
  return false;
59523
59523
  }
59524
59524
  //#endregion
59525
+ //#region src/launcher-status.ts
59526
+ const MAX_READ_BYTES = 256 * 1024;
59527
+ const MAX_RECORD_BYTES = 8192;
59528
+ const MAX_RECEIPTS = 128;
59529
+ const MAX_SENDS = 16;
59530
+ const MAX_RECORDS = 2048;
59531
+ const CHECKPOINT_SCHEMA = "blun.launcher-status-checkpoint/v2";
59532
+ function resolveStatusChatId(access, configuredChatId) {
59533
+ const allowed = (chatId) => /^-?[1-9]\d*$/.test(chatId) && (Object.hasOwn(access.groups, chatId) || access.dmPolicy !== "disabled" && access.allowFrom.includes(chatId));
59534
+ const requested = configuredChatId?.trim();
59535
+ if (requested) return allowed(requested) ? requested : null;
59536
+ const groups = Object.keys(access.groups).filter(allowed);
59537
+ if (groups.length > 0) return groups.length === 1 ? groups[0] : null;
59538
+ const direct = [...new Set(access.allowFrom.filter(allowed))];
59539
+ return direct.length === 1 ? direct[0] : null;
59540
+ }
59541
+ function regularFile(file) {
59542
+ const before = lstatSync(file, { bigint: true });
59543
+ if (!before.isFile() || before.isSymbolicLink()) throw new Error("launcher-status.invalid-file");
59544
+ const fd = openSync(file, "r");
59545
+ const after = fstatSync(fd, { bigint: true });
59546
+ if (!after.isFile() || before.dev !== after.dev || before.ino !== after.ino) {
59547
+ closeSync(fd);
59548
+ throw new Error("launcher-status.file-changed");
59549
+ }
59550
+ return fd;
59551
+ }
59552
+ function openOutbox(file) {
59553
+ try {
59554
+ const stat = lstatSync(file);
59555
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) throw new Error("launcher-status.invalid-file");
59556
+ } catch (error) {
59557
+ if (error.code !== "ENOENT") throw error;
59558
+ }
59559
+ const fd = openSync(file, "a", 384);
59560
+ try {
59561
+ verifyOutbox(file, fd);
59562
+ } catch (error) {
59563
+ closeSync(fd);
59564
+ throw error;
59565
+ }
59566
+ return fd;
59567
+ }
59568
+ function verifyOutbox(file, fd) {
59569
+ const pathStat = lstatSync(file, { bigint: true });
59570
+ const handleStat = fstatSync(fd, { bigint: true });
59571
+ if (!pathStat.isFile() || pathStat.isSymbolicLink() || pathStat.nlink !== 1n || handleStat.nlink !== 1n || pathStat.dev !== handleStat.dev || pathStat.ino !== handleStat.ino) throw new Error("launcher-status.file-changed");
59572
+ }
59573
+ function readRange(fd, start, length) {
59574
+ const bytes = Buffer.alloc(length);
59575
+ const size = readSync(fd, bytes, 0, length, start);
59576
+ return bytes.subarray(0, size);
59577
+ }
59578
+ function hash(bytes) {
59579
+ return createHash("sha256").update(bytes).digest("hex");
59580
+ }
59581
+ function readSmallJson(file) {
59582
+ let fd;
59583
+ try {
59584
+ fd = regularFile(file);
59585
+ const size = fstatSync(fd).size;
59586
+ if (size > 64 * 1024) throw new Error("launcher-status.checkpoint-too-large");
59587
+ return JSON.parse(readRange(fd, 0, size).toString("utf8"));
59588
+ } catch (error) {
59589
+ if (error.code === "ENOENT" || error instanceof SyntaxError) return void 0;
59590
+ throw error;
59591
+ } finally {
59592
+ if (fd !== void 0) closeSync(fd);
59593
+ }
59594
+ }
59595
+ function writeCheckpoint(file, checkpoint) {
59596
+ const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
59597
+ try {
59598
+ writeFileSync(temporary, JSON.stringify(checkpoint) + "\n", {
59599
+ flag: "wx",
59600
+ mode: 384
59601
+ });
59602
+ renameSync(temporary, file);
59603
+ } finally {
59604
+ rmSync(temporary, { force: true });
59605
+ }
59606
+ }
59607
+ function object$1(value) {
59608
+ return typeof value === "object" && value !== null && !Array.isArray(value);
59609
+ }
59610
+ function recentReceipts(file) {
59611
+ let fd;
59612
+ try {
59613
+ fd = regularFile(file);
59614
+ const size = fstatSync(fd).size;
59615
+ const start = Math.max(0, size - MAX_READ_BYTES);
59616
+ const lines = readRange(fd, start, size - start).toString("utf8").split("\n");
59617
+ if (start > 0) lines.shift();
59618
+ const ids = [];
59619
+ for (let i = lines.length - 1, count = 0; i >= 0 && count < MAX_RECORDS; i--, count++) {
59620
+ const line = lines[i];
59621
+ if (Buffer.byteLength(line) > MAX_RECORD_BYTES) continue;
59622
+ try {
59623
+ const record = JSON.parse(line);
59624
+ if (object$1(record) && record.kind === "launcher-status" && Number.isSafeInteger(record.message_id) && typeof record.event_id === "string" && record.event_id.length <= 128) ids.push(record.event_id);
59625
+ } catch {}
59626
+ }
59627
+ return ids.slice(0, MAX_RECEIPTS).reverse();
59628
+ } catch (error) {
59629
+ if (error.code === "ENOENT") return [];
59630
+ throw error;
59631
+ } finally {
59632
+ if (fd !== void 0) closeSync(fd);
59633
+ }
59634
+ }
59635
+ function parseEvent(line) {
59636
+ if (line.length > MAX_RECORD_BYTES) return void 0;
59637
+ let event;
59638
+ try {
59639
+ event = JSON.parse(line.toString("utf8"));
59640
+ } catch {
59641
+ return;
59642
+ }
59643
+ if (!object$1(event) || event.schema !== "blun.launcher-status/v1" || typeof event.id !== "string" || !/^[\w-]{1,128}$/.test(event.id) || !["launcher-restart", "launcher-restart-exhausted"].includes(String(event.kind)) || !Number.isSafeInteger(event.attempt) || !Number.isSafeInteger(event.limit) || Number(event.attempt) < 1 || Number(event.limit) < 1 || Number(event.limit) > 100 || Number(event.attempt) > Number(event.limit) || !Number.isSafeInteger(event.exitCode) || event.signal != null && (typeof event.signal !== "string" || !/^SIG[A-Z0-9]{1,16}$/.test(event.signal))) return;
59644
+ const stopped = event.kind === "launcher-restart-exhausted" ? "stopped " : "";
59645
+ const signal = event.signal ? ` signal=${event.signal}` : "";
59646
+ return {
59647
+ id: event.id,
59648
+ text: `[launcher.restart] ${stopped}${event.attempt}/${event.limit} exit=${event.exitCode}${signal}`
59649
+ };
59650
+ }
59651
+ function cursorValue(value) {
59652
+ if (!object$1(value) || typeof value.identity !== "string" || value.identity.length > 256 || !Number.isSafeInteger(value.offset) || Number(value.offset) < 0 || !Number.isSafeInteger(value.prefixBytes) || Number(value.prefixBytes) < 0 || Number(value.prefixBytes) > 128 || typeof value.prefixHash !== "string" || !/^[a-f0-9]{64}$/.test(value.prefixHash)) return void 0;
59653
+ return {
59654
+ identity: value.identity,
59655
+ offset: Number(value.offset),
59656
+ prefixBytes: Number(value.prefixBytes),
59657
+ prefixHash: value.prefixHash,
59658
+ dropping: value.dropping === true
59659
+ };
59660
+ }
59661
+ function fileCursor(fd, saved) {
59662
+ const stat = fstatSync(fd, { bigint: true });
59663
+ const identity = `${stat.dev}:${stat.ino}:${stat.birthtimeMs}`;
59664
+ const size = Number(stat.size);
59665
+ const prefixBytes = Math.min(size, 128);
59666
+ const cursor = {
59667
+ identity,
59668
+ prefixBytes,
59669
+ prefixHash: hash(readRange(fd, 0, prefixBytes)),
59670
+ offset: 0,
59671
+ dropping: false
59672
+ };
59673
+ const previous = cursorValue(saved);
59674
+ if (!previous || previous.identity !== identity || previous.offset > size || previous.prefixBytes > Math.min(128, size) || previous.prefixHash !== hash(readRange(fd, 0, previous.prefixBytes))) return {
59675
+ cursor,
59676
+ restored: false
59677
+ };
59678
+ cursor.offset = previous.offset;
59679
+ cursor.dropping = previous.dropping;
59680
+ return {
59681
+ cursor,
59682
+ restored: true
59683
+ };
59684
+ }
59685
+ function initialCheckpoint(fd, saved) {
59686
+ const { cursor, restored } = fileCursor(fd, saved);
59687
+ const checkpoint = {
59688
+ schema: CHECKPOINT_SCHEMA,
59689
+ ...cursor,
59690
+ deliveredIds: []
59691
+ };
59692
+ if (!object$1(saved)) return checkpoint;
59693
+ const size = fstatSync(fd).size;
59694
+ if (saved.schema === "blun.launcher-status-checkpoint/v1") {
59695
+ if (Number.isSafeInteger(saved.offset) && Number(saved.offset) > 0 && Number(saved.offset) <= size && readRange(fd, Number(saved.offset) - 1, 1)[0] === 10) checkpoint.legacy = { until: Number(saved.offset) };
59696
+ return checkpoint;
59697
+ }
59698
+ if (saved.schema !== CHECKPOINT_SCHEMA) return {
59699
+ ...checkpoint,
59700
+ offset: 0,
59701
+ dropping: false
59702
+ };
59703
+ if (Array.isArray(saved.deliveredIds)) checkpoint.deliveredIds = saved.deliveredIds.filter((id) => typeof id === "string" && /^[\w-]{1,128}$/.test(id)).slice(-MAX_RECEIPTS);
59704
+ if (restored && object$1(saved.legacy) && Number.isSafeInteger(saved.legacy.until) && Number(saved.legacy.until) > checkpoint.offset && Number(saved.legacy.until) <= size) {
59705
+ checkpoint.legacy = { until: Number(saved.legacy.until) };
59706
+ const receipt = cursorValue(saved.legacy.receipt);
59707
+ if (receipt && object$1(saved.legacy.receipt) && typeof saved.legacy.receipt.eventId === "string" && /^[\w-]{1,128}$/.test(saved.legacy.receipt.eventId)) checkpoint.legacy.receipt = {
59708
+ ...receipt,
59709
+ eventId: saved.legacy.receipt.eventId
59710
+ };
59711
+ }
59712
+ return checkpoint;
59713
+ }
59714
+ function confirmLegacyReceipt(file, legacy, eventId, budget, changed) {
59715
+ if (budget.bytes === 0 || budget.records === 0) return false;
59716
+ let fd;
59717
+ try {
59718
+ fd = regularFile(file);
59719
+ } catch (error) {
59720
+ if (error.code === "ENOENT") throw new Error("launcher-status.legacy-cursor-unverified");
59721
+ throw error;
59722
+ }
59723
+ try {
59724
+ const previous = legacy.receipt?.eventId === eventId ? legacy.receipt : void 0;
59725
+ const { cursor, restored } = fileCursor(fd, previous);
59726
+ if (!restored || cursor.prefixBytes !== previous?.prefixBytes) changed();
59727
+ const scan = legacy.receipt = {
59728
+ ...cursor,
59729
+ eventId
59730
+ };
59731
+ const size = fstatSync(fd).size;
59732
+ const bytes = readRange(fd, scan.offset, Math.min(budget.bytes, size - scan.offset));
59733
+ budget.bytes -= bytes.length;
59734
+ let start = 0;
59735
+ const advance = (count) => {
59736
+ scan.offset += count;
59737
+ changed();
59738
+ };
59739
+ while (start < bytes.length && budget.records > 0) {
59740
+ const newline = bytes.indexOf(10, start);
59741
+ if (newline < 0) {
59742
+ if (scan.dropping || bytes.length - start > MAX_RECORD_BYTES) {
59743
+ scan.dropping = true;
59744
+ advance(bytes.length - start);
59745
+ }
59746
+ return false;
59747
+ }
59748
+ budget.records--;
59749
+ const line = bytes.subarray(start, newline);
59750
+ const complete = !scan.dropping && line.length <= MAX_RECORD_BYTES;
59751
+ scan.dropping = false;
59752
+ advance(newline + 1 - start);
59753
+ start = newline + 1;
59754
+ if (complete) try {
59755
+ const record = JSON.parse(line.toString("utf8"));
59756
+ if (object$1(record) && record.kind === "launcher-status" && record.event_id === eventId && Number.isSafeInteger(record.message_id)) return true;
59757
+ } catch {}
59758
+ }
59759
+ if (scan.offset >= size) throw new Error("launcher-status.legacy-cursor-unverified");
59760
+ return false;
59761
+ } finally {
59762
+ closeSync(fd);
59763
+ }
59764
+ }
59765
+ const drains = /* @__PURE__ */ new Map();
59766
+ function drainLauncherStatusQueue(options) {
59767
+ const directory = resolve(options.stateDir);
59768
+ const pending = drains.get(directory);
59769
+ if (pending) return pending;
59770
+ const result = drain({
59771
+ ...options,
59772
+ stateDir: directory
59773
+ }).finally(() => drains.delete(directory));
59774
+ drains.set(directory, result);
59775
+ return result;
59776
+ }
59777
+ async function drain(options) {
59778
+ const queue = join(options.stateDir, "launcher-status-queue.jsonl");
59779
+ const cursorFile = join(options.stateDir, "launcher-status.checkpoint.json");
59780
+ const outbox = join(options.stateDir, "outbox.jsonl");
59781
+ let fd;
59782
+ try {
59783
+ fd = regularFile(queue);
59784
+ } catch (error) {
59785
+ if (error.code === "ENOENT") return { delivered: 0 };
59786
+ throw error;
59787
+ }
59788
+ try {
59789
+ const checkpoint = initialCheckpoint(fd, readSmallJson(cursorFile));
59790
+ const bytes = readRange(fd, checkpoint.offset, Math.min(MAX_READ_BYTES, fstatSync(fd).size - checkpoint.offset));
59791
+ if (bytes.length === 0) return { delivered: 0 };
59792
+ const migrating = checkpoint.legacy !== void 0;
59793
+ const ids = new Set([...migrating ? [] : recentReceipts(outbox), ...checkpoint.deliveredIds]);
59794
+ let start = 0;
59795
+ let delivered = 0;
59796
+ let records = 0;
59797
+ let dirty = false;
59798
+ const receiptBudget = {
59799
+ bytes: MAX_READ_BYTES,
59800
+ records: MAX_RECORDS
59801
+ };
59802
+ const advance = (count) => {
59803
+ checkpoint.offset += count;
59804
+ if (checkpoint.legacy && checkpoint.offset >= checkpoint.legacy.until) checkpoint.legacy = void 0;
59805
+ dirty = true;
59806
+ };
59807
+ const flush = () => {
59808
+ if (!dirty) return;
59809
+ checkpoint.deliveredIds = [...ids].slice(-MAX_RECEIPTS);
59810
+ writeCheckpoint(cursorFile, checkpoint);
59811
+ dirty = false;
59812
+ };
59813
+ try {
59814
+ while (start < bytes.length && delivered < MAX_SENDS && records < MAX_RECORDS && !options.isDisposed?.()) {
59815
+ const newline = bytes.indexOf(10, start);
59816
+ if (newline < 0) {
59817
+ if (checkpoint.dropping || bytes.length - start > MAX_RECORD_BYTES) {
59818
+ checkpoint.dropping = true;
59819
+ advance(bytes.length - start);
59820
+ }
59821
+ break;
59822
+ }
59823
+ const count = newline + 1 - start;
59824
+ records++;
59825
+ const event = checkpoint.dropping ? void 0 : parseEvent(bytes.subarray(start, newline));
59826
+ checkpoint.dropping = false;
59827
+ if (event && !ids.has(event.id) && checkpoint.legacy) {
59828
+ if (!confirmLegacyReceipt(outbox, checkpoint.legacy, event.id, receiptBudget, () => {
59829
+ dirty = true;
59830
+ })) break;
59831
+ ids.add(event.id);
59832
+ }
59833
+ if (event && !ids.has(event.id)) {
59834
+ const chatId = resolveStatusChatId(options.readAccess(), options.configuredChatId);
59835
+ if (chatId === null) break;
59836
+ const outboxFd = openOutbox(outbox);
59837
+ try {
59838
+ const sent = await options.sendMessage(chatId, event.text);
59839
+ if (!Number.isSafeInteger(sent.message_id)) throw new Error("launcher-status.missing-receipt");
59840
+ ids.add(event.id);
59841
+ delivered++;
59842
+ try {
59843
+ verifyOutbox(outbox, outboxFd);
59844
+ appendFileSync(outboxFd, JSON.stringify({
59845
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
59846
+ direction: "out",
59847
+ kind: "launcher-status",
59848
+ chat_id: chatId,
59849
+ message_id: sent.message_id,
59850
+ event_id: event.id,
59851
+ text: event.text
59852
+ }) + "\n");
59853
+ } finally {
59854
+ advance(count);
59855
+ flush();
59856
+ }
59857
+ } finally {
59858
+ closeSync(outboxFd);
59859
+ }
59860
+ } else advance(count);
59861
+ start = newline + 1;
59862
+ if (migrating && !checkpoint.legacy) break;
59863
+ }
59864
+ return { delivered };
59865
+ } finally {
59866
+ flush();
59867
+ }
59868
+ } finally {
59869
+ closeSync(fd);
59870
+ }
59871
+ }
59872
+ function startLauncherStatusWatcher(options) {
59873
+ let disposed = false;
59874
+ let active;
59875
+ const run = () => {
59876
+ if (disposed || active) return;
59877
+ active = drainLauncherStatusQueue({
59878
+ ...options,
59879
+ isDisposed: () => disposed
59880
+ }).then(() => {}, (error) => {
59881
+ options.onError?.(error);
59882
+ }).finally(() => {
59883
+ active = void 0;
59884
+ });
59885
+ active.catch(() => {});
59886
+ };
59887
+ const timer = setInterval(() => {
59888
+ run();
59889
+ }, options.intervalMs ?? 1e3);
59890
+ timer.unref();
59891
+ run();
59892
+ return { dispose() {
59893
+ disposed = true;
59894
+ clearInterval(timer);
59895
+ return active ?? Promise.resolve();
59896
+ } };
59897
+ }
59898
+ //#endregion
59525
59899
  //#region src/format.ts
59526
59900
  /**
59527
59901
  * Filenames and titles are uploader-controlled. They land inside the
@@ -68035,6 +68409,13 @@ const session = new KingSession(KING_ENTRY, (msg) => {
68035
68409
  process.stderr.write(`telegram bridge: ${msg}\n`);
68036
68410
  });
68037
68411
  const headlessContext = new BoundedChannelContextBuffer();
68412
+ const launcherStatusWatcher = startLauncherStatusWatcher({
68413
+ stateDir: stateDir(),
68414
+ readAccess,
68415
+ configuredChatId: process.env["BLUN_TELEGRAM_STATUS_CHAT_ID"],
68416
+ sendMessage: (chatId, text) => bot.api.sendMessage(chatId, text),
68417
+ onError: () => process.stderr.write("telegram bridge: launcher-status.delivery-failed\n")
68418
+ });
68038
68419
  const SEEN_FILE = join(stateDir(), "seen-inbound.json");
68039
68420
  const SEEN_MAX = 500;
68040
68421
  let seenIds = [];
@@ -68153,10 +68534,15 @@ function shutdown() {
68153
68534
  if (shuttingDown) return;
68154
68535
  shuttingDown = true;
68155
68536
  process.stderr.write("telegram bridge: shutting down\n");
68156
- releasePollerPid();
68537
+ const drained = launcherStatusWatcher.dispose();
68157
68538
  session.dispose();
68158
- setTimeout(() => process.exit(0), 2e3).unref();
68159
- Promise.resolve(bot.stop()).finally(() => process.exit(0));
68539
+ const forcedExit = setTimeout(() => process.exit(0), 2e3);
68540
+ forcedExit.unref();
68541
+ Promise.allSettled([drained, bot.stop()]).then(() => {
68542
+ releasePollerPid();
68543
+ clearTimeout(forcedExit);
68544
+ process.exit(0);
68545
+ });
68160
68546
  }
68161
68547
  process.on("SIGTERM", shutdown);
68162
68548
  process.on("SIGINT", shutdown);