be1f86044e
Phase 3 of RFC #308: Stateful Sense refactor. - Examples (cpu-usage, nerve-health) use initialState + compute(state) - CLI create/init templates generate stateful sense (no SQLite/Drizzle) - Removed: sense query, sense schema commands (no more per-sense SQLite) - Removed: sense-sqlite.ts, schema templates, migration templates - Updated all CLI tests for new sense structure Refs #308, closes #311
81 lines
2.1 KiB
TypeScript
81 lines
2.1 KiB
TypeScript
/**
|
|
* nerve-health — built-in sense that reports daemon health via IPC.
|
|
*
|
|
* When running inside a sense worker, this compute function sends a
|
|
* "health-request" to the parent kernel process and merges the response into state.
|
|
*
|
|
* Usage in nerve.yaml:
|
|
* senses:
|
|
* nerve-health:
|
|
* group: internal
|
|
* throttle: 30s
|
|
* timeout: 5s
|
|
* interval: 30s
|
|
*/
|
|
|
|
export type NerveHealth = {
|
|
uptime: number;
|
|
activeSenses: string[];
|
|
inFlightCount: number;
|
|
workerMemoryUsage: NodeJS.MemoryUsage;
|
|
workerUptime: number;
|
|
};
|
|
|
|
type HealthState = {
|
|
lastCheck: number | null;
|
|
lastHealth: NerveHealth | null;
|
|
};
|
|
|
|
export const initialState: HealthState = { lastCheck: null, lastHealth: null };
|
|
|
|
export async function compute(_state: HealthState): Promise<{
|
|
state: HealthState;
|
|
workflow: null;
|
|
}> {
|
|
void _state;
|
|
const health = await requestHealthFromKernel();
|
|
return {
|
|
state: { lastCheck: Date.now(), lastHealth: health },
|
|
workflow: null,
|
|
};
|
|
}
|
|
|
|
function requestHealthFromKernel(): Promise<NerveHealth> {
|
|
return new Promise((resolve, reject) => {
|
|
if (!process.send) {
|
|
reject(new Error("nerve-health: not running as a child process with IPC"));
|
|
return;
|
|
}
|
|
|
|
const timeout = setTimeout(() => {
|
|
process.removeListener("message", onMessage);
|
|
reject(new Error("nerve-health: health-request timed out"));
|
|
}, 5000);
|
|
|
|
function onMessage(msg: unknown): void {
|
|
if (
|
|
msg !== null &&
|
|
typeof msg === "object" &&
|
|
(msg as Record<string, unknown>).type === "health-response"
|
|
) {
|
|
clearTimeout(timeout);
|
|
process.removeListener("message", onMessage);
|
|
const resp = msg as {
|
|
senses: string[];
|
|
inFlightCount: number;
|
|
};
|
|
resolve({
|
|
uptime: process.uptime(),
|
|
activeSenses: resp.senses,
|
|
inFlightCount: resp.inFlightCount,
|
|
workerMemoryUsage: process.memoryUsage(),
|
|
workerUptime: process.uptime(),
|
|
});
|
|
}
|
|
}
|
|
|
|
process.on("message", onMessage);
|
|
process.send({ type: "health-request" });
|
|
});
|
|
}
|