feat(cvm-server): heartbeat re-announce, wire LOG_LEVEL, fix from_date
- Heartbeat: re-publish discovery announcements every CVM_HEARTBEAT_MS (15 min) via the SDK's announcement manager (defensively reached), so recency-based health checks stay green for a long-running process. - LOG_LEVEL is now actually passed to the transport (was documented but unused). - find_events: from_date was a no-op (filter(() => true)); now filters by the kind-31923 start tag. - .env.example: fix CVM_RELAY (.org), drop the dead wotr relay, document CVM_HEARTBEAT_MS.
This commit is contained in:
parent
18cc8218b2
commit
2bc903d128
2 changed files with 50 additions and 9 deletions
|
|
@ -7,11 +7,15 @@
|
|||
# NEVER commit this to git!
|
||||
CVM_SERVER_NSEC_HEX=your_64_char_hex_private_key_here
|
||||
|
||||
# Nostr relays to query for skate data
|
||||
NOSTR_RELAYS=wss://goskatespots.nostr1.com,wss://relay.ditto.pub,wss://wotr.relatr.xyz
|
||||
# Nostr relays to query for skate data (comma-separated)
|
||||
NOSTR_RELAYS=wss://goskatespots.nostr1.com,wss://relay.ditto.pub
|
||||
|
||||
# CVM discovery relay — where agents find your server
|
||||
CVM_RELAY=wss://relay.contextvm.net
|
||||
CVM_RELAY=wss://relay.contextvm.org
|
||||
|
||||
# Log level: info | debug | warn | error
|
||||
# Log level passed to the ContextVM transport: debug | info | warn | error | silent
|
||||
LOG_LEVEL=info
|
||||
|
||||
# How often to re-publish the discovery announcements, in ms (0 disables).
|
||||
# Keeps recency-based health checks green between (re)starts.
|
||||
CVM_HEARTBEAT_MS=900000
|
||||
|
|
|
|||
|
|
@ -44,6 +44,16 @@ const NOSTR_RELAYS = (process.env.NOSTR_RELAYS || "wss://goskatespots.nostr1.com
|
|||
|
||||
const CVM_RELAY = process.env.CVM_RELAY || "wss://relay.contextvm.org";
|
||||
|
||||
// Transport log level. Previously documented but never consumed (always the
|
||||
// SDK default); now actually wired through to the transport.
|
||||
type SdkLogLevel = "debug" | "info" | "warn" | "error" | "silent";
|
||||
const LOG_LEVEL = (process.env.LOG_LEVEL || "info") as SdkLogLevel;
|
||||
|
||||
// How often to re-publish the server's discovery announcements. The SDK only
|
||||
// announces on start(), so without this a long-running server looks "stale" to
|
||||
// recency-based health checks. Set 0 to disable.
|
||||
const HEARTBEAT_MS = Number(process.env.CVM_HEARTBEAT_MS || 15 * 60 * 1000);
|
||||
|
||||
// ─── Nostr data pool ─────────────────────────────────────────────────────────
|
||||
// Used to query skate data from the community relays
|
||||
|
||||
|
|
@ -303,11 +313,13 @@ mcpServer.registerTool(
|
|||
|
||||
if (from_date) {
|
||||
const fromTs = Math.floor(new Date(from_date).getTime() / 1000);
|
||||
events = events.filter(() => {
|
||||
// start tag is unix timestamp for kind 31923
|
||||
return true; // already filtered by since: now above
|
||||
});
|
||||
void fromTs;
|
||||
if (Number.isFinite(fromTs)) {
|
||||
// kind 31923 stores its start as a unix timestamp in the `start` tag.
|
||||
events = events.filter((e) => {
|
||||
const start = parseInt(getTag(e, "start") ?? "0");
|
||||
return !start || start >= fromTs;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (to_date) {
|
||||
|
|
@ -606,6 +618,7 @@ async function main() {
|
|||
signer,
|
||||
relayHandler: cvmRelayPool,
|
||||
isAnnouncedServer: true,
|
||||
logLevel: LOG_LEVEL,
|
||||
serverInfo: {
|
||||
name: "SkateSpots Data Server",
|
||||
about:
|
||||
|
|
@ -617,6 +630,30 @@ async function main() {
|
|||
|
||||
await mcpServer.connect(transport);
|
||||
|
||||
// ─── Heartbeat ─────────────────────────────────────────────────────────────
|
||||
// Re-publish the server announcements periodically so recency-based health
|
||||
// checks stay green for a long-running process. The announcement manager is a
|
||||
// typed-private SDK member, so we reach it defensively: if a future SDK
|
||||
// renames it we log and skip (health checks then fall back to restart-cadence
|
||||
// freshness) instead of crashing the server.
|
||||
const announcementManager = (
|
||||
transport as unknown as {
|
||||
announcementManager?: { publishPublicAnnouncements?: () => Promise<void> };
|
||||
}
|
||||
).announcementManager;
|
||||
|
||||
if (HEARTBEAT_MS > 0 && typeof announcementManager?.publishPublicAnnouncements === "function") {
|
||||
const heartbeat = setInterval(() => {
|
||||
announcementManager.publishPublicAnnouncements!().catch((e) =>
|
||||
console.error("[heartbeat] re-announce failed:", e)
|
||||
);
|
||||
}, HEARTBEAT_MS);
|
||||
(heartbeat as { unref?: () => void }).unref?.();
|
||||
console.log(` Heartbeat : every ${Math.round(HEARTBEAT_MS / 60000)} min`);
|
||||
} else if (HEARTBEAT_MS > 0) {
|
||||
console.warn(" ⚠️ Heartbeat unavailable — SDK internals changed; health checks may show stale.");
|
||||
}
|
||||
|
||||
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||
console.log(" ✅ Server running — listening for agents on Nostr");
|
||||
console.log("");
|
||||
|
|
|
|||
Loading…
Reference in a new issue