@threadbase-sh/streamer 1.65.0 → 1.66.0
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/api/routes/misc.routes.d.ts.map +1 -1
- package/dist/api/types/api-deps.d.ts +2 -0
- package/dist/api/types/api-deps.d.ts.map +1 -1
- package/dist/cli.cjs +245 -14
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +245 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +233 -2
- package/dist/index.js.map +1 -1
- package/dist/server-wiring.d.ts +2 -0
- package/dist/server-wiring.d.ts.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/services/host-pressure/hostPressure.d.ts +83 -0
- package/dist/services/host-pressure/hostPressure.d.ts.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4926,7 +4926,7 @@ function redactPath(path) {
|
|
|
4926
4926
|
function worstStatus(checks) {
|
|
4927
4927
|
const rank = { ok: 0, unknown: 1, degraded: 2, failed: 3 };
|
|
4928
4928
|
return checks.reduce(
|
|
4929
|
-
(
|
|
4929
|
+
(worst2, c) => rank[c.status] > rank[worst2] ? c.status : worst2,
|
|
4930
4930
|
"ok"
|
|
4931
4931
|
);
|
|
4932
4932
|
}
|
|
@@ -5946,7 +5946,12 @@ var createMiscRoutes = (deps) => {
|
|
|
5946
5946
|
// Whether to encrypt to this server. Additive, same contract as `push`:
|
|
5947
5947
|
// absent means an older server, which a client must read as "unknown" and
|
|
5948
5948
|
// resolve as today's plaintext path — never as a reason to fail.
|
|
5949
|
-
e2ee: describeE2eeCapability(deps.featureFlagsConfig().values.e2ee)
|
|
5949
|
+
e2ee: describeE2eeCapability(deps.featureFlagsConfig().values.e2ee),
|
|
5950
|
+
// This build samples cheap host signals and pushes `host_pressure` when
|
|
5951
|
+
// the box is starved. Additive capability flag only — live readings stay
|
|
5952
|
+
// off this polled endpoint. Absent means an older server that never
|
|
5953
|
+
// samples. Informational: pressure never holds, kills, or refuses sessions.
|
|
5954
|
+
hostPressure: true
|
|
5950
5955
|
});
|
|
5951
5956
|
});
|
|
5952
5957
|
app.get("/api/profiles", (c) => c.json([]));
|
|
@@ -12851,6 +12856,7 @@ function createApiDeps(deps) {
|
|
|
12851
12856
|
wsHub: deps.wsHub,
|
|
12852
12857
|
cache: () => deps.cache(),
|
|
12853
12858
|
cacheMonitor: () => deps.cacheMonitor(),
|
|
12859
|
+
hostPressureMonitor: () => deps.hostPressureMonitor(),
|
|
12854
12860
|
pushRepo: () => deps.pushRepo(),
|
|
12855
12861
|
liveActivityPushEnabled: () => deps.liveActivityPushEnabled(),
|
|
12856
12862
|
devicesRepo: () => deps.devicesRepo(),
|
|
@@ -12901,6 +12907,8 @@ function createApiDeps(deps) {
|
|
|
12901
12907
|
}
|
|
12902
12908
|
const alertMsg = deps.cacheMonitor()?.wsMessage();
|
|
12903
12909
|
if (alertMsg) deps.wsHub.unicast(ws, alertMsg);
|
|
12910
|
+
const pressureMsg = deps.hostPressureMonitor()?.wsMessage();
|
|
12911
|
+
if (pressureMsg) deps.wsHub.unicast(ws, pressureMsg);
|
|
12904
12912
|
},
|
|
12905
12913
|
handleWsMessage: async (ws, raw, principal) => {
|
|
12906
12914
|
const deny = (type, required) => {
|
|
@@ -13526,6 +13534,221 @@ function pruneAgentConversations(cache) {
|
|
|
13526
13534
|
return { scanned: rows.length, pruned, missing };
|
|
13527
13535
|
}
|
|
13528
13536
|
|
|
13537
|
+
// src/services/host-pressure/hostPressure.ts
|
|
13538
|
+
import { cpus, freemem, loadavg, totalmem } from "os";
|
|
13539
|
+
import { monitorEventLoopDelay } from "perf_hooks";
|
|
13540
|
+
var HOST_PRESSURE_SAMPLE_MS = 5e3;
|
|
13541
|
+
var HOST_PRESSURE_BARS = {
|
|
13542
|
+
memFreeRatio: {
|
|
13543
|
+
enterElevated: 0.15,
|
|
13544
|
+
leaveElevated: 0.17,
|
|
13545
|
+
enterCritical: 0.08,
|
|
13546
|
+
leaveCritical: 0.15
|
|
13547
|
+
},
|
|
13548
|
+
eventLoopP99Ms: {
|
|
13549
|
+
enterElevated: 100,
|
|
13550
|
+
leaveElevated: 80,
|
|
13551
|
+
enterCritical: 250,
|
|
13552
|
+
leaveCritical: 100
|
|
13553
|
+
},
|
|
13554
|
+
loadPerCpu: {
|
|
13555
|
+
enterElevated: 1.25,
|
|
13556
|
+
leaveElevated: 1.05,
|
|
13557
|
+
enterCritical: 2,
|
|
13558
|
+
leaveCritical: 1.25
|
|
13559
|
+
},
|
|
13560
|
+
// win32 has no loadavg. Busy ratio from os.cpus()[].times deltas is 0–1, so
|
|
13561
|
+
// it cannot reuse loadPerCpu's 2.0 critical bar. Reason on the wire stays `load`.
|
|
13562
|
+
cpuBusy: {
|
|
13563
|
+
enterElevated: 0.85,
|
|
13564
|
+
leaveElevated: 0.7,
|
|
13565
|
+
enterCritical: 0.97,
|
|
13566
|
+
leaveCritical: 0.85
|
|
13567
|
+
},
|
|
13568
|
+
liveAgentsPair: 4
|
|
13569
|
+
};
|
|
13570
|
+
var REASON_ORDER = ["memory", "event_loop", "load", "agents"];
|
|
13571
|
+
var RANK = { ok: 0, elevated: 1, critical: 2 };
|
|
13572
|
+
function worst(levels) {
|
|
13573
|
+
return levels.reduce(
|
|
13574
|
+
(acc, level) => RANK[level] > RANK[acc] ? level : acc,
|
|
13575
|
+
"ok"
|
|
13576
|
+
);
|
|
13577
|
+
}
|
|
13578
|
+
function schmittLowIsWorse(value, previous, enterElevated, leaveElevated, enterCritical, leaveCritical) {
|
|
13579
|
+
if (previous === "critical") {
|
|
13580
|
+
if (value < leaveCritical) return "critical";
|
|
13581
|
+
if (value < leaveElevated) return "elevated";
|
|
13582
|
+
return "ok";
|
|
13583
|
+
}
|
|
13584
|
+
if (previous === "elevated") {
|
|
13585
|
+
if (value < enterCritical) return "critical";
|
|
13586
|
+
if (value < leaveElevated) return "elevated";
|
|
13587
|
+
return "ok";
|
|
13588
|
+
}
|
|
13589
|
+
if (value < enterCritical) return "critical";
|
|
13590
|
+
if (value < enterElevated) return "elevated";
|
|
13591
|
+
return "ok";
|
|
13592
|
+
}
|
|
13593
|
+
function schmittHighIsWorse(value, previous, enterElevated, leaveElevated, enterCritical, leaveCritical) {
|
|
13594
|
+
if (previous === "critical") {
|
|
13595
|
+
if (value > leaveCritical) return "critical";
|
|
13596
|
+
if (value > leaveElevated) return "elevated";
|
|
13597
|
+
return "ok";
|
|
13598
|
+
}
|
|
13599
|
+
if (previous === "elevated") {
|
|
13600
|
+
if (value > enterCritical) return "critical";
|
|
13601
|
+
if (value > leaveElevated) return "elevated";
|
|
13602
|
+
return "ok";
|
|
13603
|
+
}
|
|
13604
|
+
if (value > enterCritical) return "critical";
|
|
13605
|
+
if (value > enterElevated) return "elevated";
|
|
13606
|
+
return "ok";
|
|
13607
|
+
}
|
|
13608
|
+
function timesTotal(times) {
|
|
13609
|
+
return times.user + times.nice + times.sys + times.idle + times.irq;
|
|
13610
|
+
}
|
|
13611
|
+
function hostPressureOs(platform3) {
|
|
13612
|
+
if (platform3 === "darwin" || platform3 === "linux" || platform3 === "win32") return platform3;
|
|
13613
|
+
return void 0;
|
|
13614
|
+
}
|
|
13615
|
+
function cpuBusyRatio(previous, next) {
|
|
13616
|
+
if (!previous || previous.length === 0 || next.length === 0 || previous.length !== next.length) {
|
|
13617
|
+
return 0;
|
|
13618
|
+
}
|
|
13619
|
+
let idle = 0;
|
|
13620
|
+
let total = 0;
|
|
13621
|
+
for (let i = 0; i < next.length; i++) {
|
|
13622
|
+
const dt = timesTotal(next[i]) - timesTotal(previous[i]);
|
|
13623
|
+
if (dt <= 0) continue;
|
|
13624
|
+
total += dt;
|
|
13625
|
+
idle += Math.max(0, next[i].idle - previous[i].idle);
|
|
13626
|
+
}
|
|
13627
|
+
if (total <= 0) return 0;
|
|
13628
|
+
return 1 - idle / total;
|
|
13629
|
+
}
|
|
13630
|
+
function classifyHostPressure(sample, previous, platform3) {
|
|
13631
|
+
const memRaw = schmittLowIsWorse(
|
|
13632
|
+
sample.memFreeRatio,
|
|
13633
|
+
previous,
|
|
13634
|
+
HOST_PRESSURE_BARS.memFreeRatio.enterElevated,
|
|
13635
|
+
HOST_PRESSURE_BARS.memFreeRatio.leaveElevated,
|
|
13636
|
+
HOST_PRESSURE_BARS.memFreeRatio.enterCritical,
|
|
13637
|
+
HOST_PRESSURE_BARS.memFreeRatio.leaveCritical
|
|
13638
|
+
);
|
|
13639
|
+
const mem = platform3 === "darwin" && memRaw === "critical" ? "elevated" : memRaw;
|
|
13640
|
+
const eventLoop = schmittHighIsWorse(
|
|
13641
|
+
sample.eventLoopP99Ms,
|
|
13642
|
+
previous,
|
|
13643
|
+
HOST_PRESSURE_BARS.eventLoopP99Ms.enterElevated,
|
|
13644
|
+
HOST_PRESSURE_BARS.eventLoopP99Ms.leaveElevated,
|
|
13645
|
+
HOST_PRESSURE_BARS.eventLoopP99Ms.enterCritical,
|
|
13646
|
+
HOST_PRESSURE_BARS.eventLoopP99Ms.leaveCritical
|
|
13647
|
+
);
|
|
13648
|
+
const ncpu = sample.ncpu > 0 ? sample.ncpu : 1;
|
|
13649
|
+
const windows = platform3 === "win32";
|
|
13650
|
+
const loadValue = windows ? sample.cpuBusyRatio ?? 0 : sample.load1 / ncpu;
|
|
13651
|
+
const loadBars = windows ? HOST_PRESSURE_BARS.cpuBusy : HOST_PRESSURE_BARS.loadPerCpu;
|
|
13652
|
+
const load = schmittHighIsWorse(
|
|
13653
|
+
loadValue,
|
|
13654
|
+
previous,
|
|
13655
|
+
loadBars.enterElevated,
|
|
13656
|
+
loadBars.leaveElevated,
|
|
13657
|
+
loadBars.enterCritical,
|
|
13658
|
+
loadBars.leaveCritical
|
|
13659
|
+
);
|
|
13660
|
+
const resourceLevel = worst([mem, eventLoop, load]);
|
|
13661
|
+
const agentsPair = sample.liveAgents >= HOST_PRESSURE_BARS.liveAgentsPair && resourceLevel !== "ok";
|
|
13662
|
+
const level = resourceLevel;
|
|
13663
|
+
const firing = [];
|
|
13664
|
+
if (mem !== "ok") firing.push("memory");
|
|
13665
|
+
if (eventLoop !== "ok") firing.push("event_loop");
|
|
13666
|
+
if (load !== "ok") firing.push("load");
|
|
13667
|
+
if (agentsPair) firing.push("agents");
|
|
13668
|
+
const reasons = REASON_ORDER.filter((reason) => firing.includes(reason));
|
|
13669
|
+
return { level, reasons };
|
|
13670
|
+
}
|
|
13671
|
+
var HostPressureMonitor = class {
|
|
13672
|
+
constructor(opts) {
|
|
13673
|
+
this.opts = opts;
|
|
13674
|
+
}
|
|
13675
|
+
opts;
|
|
13676
|
+
timer = null;
|
|
13677
|
+
level = "ok";
|
|
13678
|
+
lastWarning = null;
|
|
13679
|
+
start() {
|
|
13680
|
+
this.opts.histogram?.enable();
|
|
13681
|
+
if (this.timer) return;
|
|
13682
|
+
const intervalMs = this.opts.intervalMs ?? HOST_PRESSURE_SAMPLE_MS;
|
|
13683
|
+
this.timer = setInterval(() => this.tick(), intervalMs);
|
|
13684
|
+
this.timer.unref?.();
|
|
13685
|
+
}
|
|
13686
|
+
tick() {
|
|
13687
|
+
const sample = this.opts.readSample();
|
|
13688
|
+
const platform3 = this.opts.platform ?? process.platform;
|
|
13689
|
+
const classified = classifyHostPressure(sample, this.level, platform3);
|
|
13690
|
+
if (classified.level === this.level) return;
|
|
13691
|
+
this.level = classified.level;
|
|
13692
|
+
const updatedAt = (this.opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
13693
|
+
if (classified.level === "ok") {
|
|
13694
|
+
this.lastWarning = null;
|
|
13695
|
+
this.opts.wsHub.broadcast({ type: "host_pressure_cleared", updatedAt });
|
|
13696
|
+
return;
|
|
13697
|
+
}
|
|
13698
|
+
const os2 = hostPressureOs(platform3);
|
|
13699
|
+
const message = {
|
|
13700
|
+
type: "host_pressure",
|
|
13701
|
+
level: classified.level,
|
|
13702
|
+
reasons: classified.reasons,
|
|
13703
|
+
liveAgents: sample.liveAgents,
|
|
13704
|
+
updatedAt,
|
|
13705
|
+
...os2 ? { os: os2 } : {}
|
|
13706
|
+
};
|
|
13707
|
+
this.lastWarning = message;
|
|
13708
|
+
this.opts.wsHub.broadcast(message);
|
|
13709
|
+
}
|
|
13710
|
+
wsMessage() {
|
|
13711
|
+
return this.lastWarning;
|
|
13712
|
+
}
|
|
13713
|
+
dispose() {
|
|
13714
|
+
if (this.timer) {
|
|
13715
|
+
clearInterval(this.timer);
|
|
13716
|
+
this.timer = null;
|
|
13717
|
+
}
|
|
13718
|
+
this.opts.histogram?.disable();
|
|
13719
|
+
}
|
|
13720
|
+
};
|
|
13721
|
+
function createHostPressureMonitor(wsHub, liveAgents) {
|
|
13722
|
+
const histogram = monitorEventLoopDelay({ resolution: 20 });
|
|
13723
|
+
const windows = process.platform === "win32";
|
|
13724
|
+
let prevCpuTimes = null;
|
|
13725
|
+
const monitor = new HostPressureMonitor({
|
|
13726
|
+
wsHub,
|
|
13727
|
+
histogram,
|
|
13728
|
+
readSample: () => {
|
|
13729
|
+
const eventLoopP99Ms = histogram.percentile(99) / 1e6;
|
|
13730
|
+
histogram.reset();
|
|
13731
|
+
const total = totalmem();
|
|
13732
|
+
const cpuList = cpus();
|
|
13733
|
+
const sample = {
|
|
13734
|
+
liveAgents: liveAgents(),
|
|
13735
|
+
memFreeRatio: total > 0 ? freemem() / total : 1,
|
|
13736
|
+
eventLoopP99Ms,
|
|
13737
|
+
load1: loadavg()[0],
|
|
13738
|
+
ncpu: cpuList.length
|
|
13739
|
+
};
|
|
13740
|
+
if (windows) {
|
|
13741
|
+
const cpuTimes = cpuList.map((cpu) => cpu.times);
|
|
13742
|
+
sample.cpuBusyRatio = cpuBusyRatio(prevCpuTimes, cpuTimes);
|
|
13743
|
+
prevCpuTimes = cpuTimes;
|
|
13744
|
+
}
|
|
13745
|
+
return sample;
|
|
13746
|
+
}
|
|
13747
|
+
});
|
|
13748
|
+
monitor.start();
|
|
13749
|
+
return monitor;
|
|
13750
|
+
}
|
|
13751
|
+
|
|
13529
13752
|
// src/services/push/expoPushSender.ts
|
|
13530
13753
|
var log5 = getLogger("expo-push");
|
|
13531
13754
|
var EXPO_PUSH_ENDPOINT = "https://exp.host/--/api/v2/push/send";
|
|
@@ -15574,6 +15797,7 @@ var StreamerServer = class {
|
|
|
15574
15797
|
wsToClientId = /* @__PURE__ */ new Map();
|
|
15575
15798
|
cache = null;
|
|
15576
15799
|
cacheMonitor = null;
|
|
15800
|
+
hostPressureMonitor = null;
|
|
15577
15801
|
projectsRepo = null;
|
|
15578
15802
|
conversationsRepo = null;
|
|
15579
15803
|
sessionsRepo = null;
|
|
@@ -15924,6 +16148,7 @@ var StreamerServer = class {
|
|
|
15924
16148
|
// reset-and-rescan, and tests swap methods on the server instance.
|
|
15925
16149
|
cache: () => this.cache,
|
|
15926
16150
|
cacheMonitor: () => this.cacheMonitor,
|
|
16151
|
+
hostPressureMonitor: () => this.hostPressureMonitor,
|
|
15927
16152
|
pushRepo: () => this.pushRepo,
|
|
15928
16153
|
liveActivityPushEnabled: () => this.liveActivityNotifier !== null,
|
|
15929
16154
|
devicesRepo: () => this.devicesRepo,
|
|
@@ -16398,6 +16623,10 @@ var StreamerServer = class {
|
|
|
16398
16623
|
this.idleReaperTimer = setInterval(() => this.reapIdleSessions(), IDLE_REAP_SWEEP_MS);
|
|
16399
16624
|
this.idleReaperTimer.unref?.();
|
|
16400
16625
|
}
|
|
16626
|
+
this.hostPressureMonitor = createHostPressureMonitor(
|
|
16627
|
+
this.wsHub,
|
|
16628
|
+
() => this.ptyAttachedIds().size
|
|
16629
|
+
);
|
|
16401
16630
|
const warmUp = new Promise((resolveWarm) => {
|
|
16402
16631
|
{
|
|
16403
16632
|
this.log.info(`Streamer server listening on port ${port}`, {
|
|
@@ -16710,6 +16939,8 @@ var StreamerServer = class {
|
|
|
16710
16939
|
clearInterval(this.idleReaperTimer);
|
|
16711
16940
|
this.idleReaperTimer = null;
|
|
16712
16941
|
}
|
|
16942
|
+
this.hostPressureMonitor?.dispose();
|
|
16943
|
+
this.hostPressureMonitor = null;
|
|
16713
16944
|
this.lastAgentChunkAt.clear();
|
|
16714
16945
|
this.terminalSeq.clear();
|
|
16715
16946
|
if (this.ptyManager.isRemote()) this.ptyManager.dispose();
|