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.
- package/CHANGELOG.md +36 -0
- package/README.md +12 -0
- package/agent-spine-plugin/CHANGELOG.md +1 -0
- package/agent-spine-plugin/docs/host-integration.md +15 -1
- package/agent-spine-plugin/docs/preflight-recall.md +1 -1
- package/agent-spine-plugin/scripts/check-install-hook.js +6 -5
- package/agent-spine-plugin/scripts/check-install-king.js +37 -0
- package/agent-spine-plugin/scripts/check-install.js +6 -1
- package/agent-spine-plugin/scripts/release-check.js +3 -2
- package/agent-spine-plugin/src/cli-agent.js +20 -1
- package/agent-spine-plugin/src/cli.js +1 -0
- package/agent-spine-plugin/src/index.js +1 -1
- package/agent-spine-plugin/src/lib/delivery-command-actions.js +16 -7
- package/agent-spine-plugin/src/lib/gateway-control.js +69 -1
- package/agent-spine-plugin/src/lib/gateway-host-fencing.js +92 -0
- package/agent-spine-plugin/src/lib/gateway-host-lifecycle.js +9 -1
- package/agent-spine-plugin/src/lib/gateway-prepared-host.js +28 -0
- package/agent-spine-plugin/src/lib/gateway-runs.js +46 -10
- package/agent-spine-plugin/src/lib/gateway-runtime.js +1 -1
- package/agent-spine-plugin/src/lib/gateway-state.js +3 -1
- package/agent-spine-plugin/src/lib/hook-context.js +8 -3
- package/agent-spine-plugin/src/lib/hook-output.js +2 -3
- package/agent-spine-plugin/src/lib/hook-process-advisory.js +1 -2
- package/agent-spine-plugin/src/lib/host-instruction-budget.js +17 -0
- package/agent-spine-plugin/src/lib/preflight.js +5 -19
- package/agent-spine-plugin/src/lib/source-roots.js +3 -2
- package/agent-spine-plugin/src/worker.js +30 -12
- package/bin/agentspine-king-goal-inbox.mjs +127 -0
- package/bin/agentspine-king-goal-intake.mjs +106 -0
- package/bin/agentspine-king-host-runner.mjs +109 -0
- package/bin/agentspine-king-snapshot-policy.cjs +80 -0
- package/bin/agentspine-king-status-policy.mjs +226 -0
- package/bin/agentspine-king-worker-host.mjs +160 -0
- package/bin/core-bootstrap.js +20 -3
- package/bin/launcher-mode.js +38 -1
- package/bin/launcher-restart-policy.cjs +131 -0
- package/bin/launcher-runtime.js +48 -9
- package/bin/runtime-exit-ledger.cjs +144 -0
- package/bin/runtime-exit-ledger.d.cts +23 -0
- package/bin/windows-node-crash-dump.cjs +289 -0
- package/blun.mjs +83241 -74234
- package/bundled-agent-sources.json +109 -34
- package/codebase-index/codebase_index.py +470 -0
- package/package.json +6 -1
- package/telegram-plugin/dist/bridge.mjs +390 -4
- 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
|
-
|
|
68537
|
+
const drained = launcherStatusWatcher.dispose();
|
|
68157
68538
|
session.dispose();
|
|
68158
|
-
setTimeout(() => process.exit(0), 2e3)
|
|
68159
|
-
|
|
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);
|