feat(instances): beacon on connect and relay self-entry

main
Peter Steinberger 2025-12-09 03:56:04 +01:00
parent 280c7c851f
commit 38c4f4f76c
4 changed files with 1798 additions and 1747 deletions

View File

@ -1,5 +1,6 @@
import Foundation
import OSLog
import SwiftUI
struct ControlHeartbeatEvent: Codable {
let ts: Double
@ -91,6 +92,7 @@ final class ControlChannel: ObservableObject {
self.state = .connecting
try await AgentRPC.shared.start()
self.state = .connected
PresenceReporter.shared.sendImmediate(reason: "connect")
} catch {
self.state = .degraded(error.localizedDescription)
}

View File

@ -39,6 +39,11 @@ final class PresenceReporter {
}
}
/// Fire an immediate presence beacon (e.g., right after connecting).
func sendImmediate(reason: String = "connect") {
Task { await self.push(reason: reason) }
}
private static func composePresenceSummary(mode: String, reason: String) -> String {
let host = Host.current().localizedName ?? "unknown-host"
let ip = Self.primaryIPv4Address() ?? "ip-unknown"

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
import os from "node:os";
export type SystemPresence = {
host?: string;
ip?: string;
@ -11,6 +13,45 @@ export type SystemPresence = {
const entries = new Map<string, SystemPresence>();
function resolvePrimaryIPv4(): string | undefined {
const nets = os.networkInterfaces();
const prefer = ["en0", "eth0"];
const pick = (names: string[]) => {
for (const name of names) {
const list = nets[name];
const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
if (entry?.address) return entry.address;
}
for (const list of Object.values(nets)) {
const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
if (entry?.address) return entry.address;
}
return undefined;
};
return pick(prefer) ?? os.hostname();
}
function initSelfPresence() {
const host = os.hostname();
const ip = resolvePrimaryIPv4() ?? undefined;
const version =
process.env.CLAWDIS_VERSION ?? process.env.npm_package_version ?? "unknown";
const text = `Relay: ${host}${ip ? ` (${ip})` : ""} · app ${version} · mode relay · reason self`;
const selfEntry: SystemPresence = {
host,
ip,
version,
mode: "relay",
reason: "self",
text,
ts: Date.now(),
};
const key = host.toLowerCase();
entries.set(key, selfEntry);
}
initSelfPresence();
function parsePresence(text: string): SystemPresence {
const trimmed = text.trim();
const pattern =