relay: add control channel and heartbeat stream

This commit is contained in:
Peter Steinberger
2025-12-08 21:50:24 +01:00
parent 71e58c768c
commit 22996854f7
7 changed files with 330 additions and 135 deletions

View File

@@ -9,21 +9,21 @@ import {
webAuthExists,
} from "../web/session.js";
const formatAge = (ms: number | null | undefined) => {
if (!ms || ms < 0) return "unknown";
const minutes = Math.round(ms / 60_000);
if (minutes < 1) return "just now";
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.round(minutes / 60);
if (hours < 48) return `${hours}h ago`;
const days = Math.round(hours / 24);
return `${days}d ago`;
export type StatusSummary = {
web: { linked: boolean; authAgeMs: number | null };
heartbeatSeconds: number;
sessions: {
path: string;
count: number;
recent: Array<{
key: string;
updatedAt: number | null;
age: number | null;
}>;
};
};
export async function statusCommand(
opts: { json?: boolean },
runtime: RuntimeEnv,
) {
export async function getStatusSummary(): Promise<StatusSummary> {
const cfg = loadConfig();
const linked = await webAuthExists();
const authAgeMs = getWebAuthAgeMs();
@@ -41,18 +41,33 @@ export async function statusCommand(
age: s.updatedAt ? Date.now() - s.updatedAt : null,
}));
const summary = {
web: {
linked,
authAgeMs,
},
return {
web: { linked, authAgeMs },
heartbeatSeconds,
sessions: {
path: storePath,
count: sessions.length,
recent,
},
} as const;
};
}
const formatAge = (ms: number | null | undefined) => {
if (!ms || ms < 0) return "unknown";
const minutes = Math.round(ms / 60_000);
if (minutes < 1) return "just now";
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.round(minutes / 60);
if (hours < 48) return `${hours}h ago`;
const days = Math.round(hours / 24);
return `${days}d ago`;
};
export async function statusCommand(
opts: { json?: boolean },
runtime: RuntimeEnv,
) {
const summary = await getStatusSummary();
if (opts.json) {
runtime.log(JSON.stringify(summary, null, 2));
@@ -60,17 +75,17 @@ export async function statusCommand(
}
runtime.log(
`Web session: ${linked ? "linked" : "not linked"}${linked ? ` (last refreshed ${formatAge(authAgeMs)})` : ""}`,
`Web session: ${summary.web.linked ? "linked" : "not linked"}${summary.web.linked ? ` (last refreshed ${formatAge(summary.web.authAgeMs)})` : ""}`,
);
if (linked) {
if (summary.web.linked) {
logWebSelfId(runtime, true);
}
runtime.log(info(`Heartbeat: ${heartbeatSeconds}s`));
runtime.log(info(`Session store: ${storePath}`));
runtime.log(info(`Active sessions: ${sessions.length}`));
if (recent.length > 0) {
runtime.log(info(`Heartbeat: ${summary.heartbeatSeconds}s`));
runtime.log(info(`Session store: ${summary.sessions.path}`));
runtime.log(info(`Active sessions: ${summary.sessions.count}`));
if (summary.sessions.recent.length > 0) {
runtime.log("Recent sessions:");
for (const r of recent) {
for (const r of summary.sessions.recent) {
runtime.log(
`- ${r.key} (${r.updatedAt ? formatAge(Date.now() - r.updatedAt) : "no activity"})`,
);