fix: use raius pi id for persistence
This commit is contained in:
@@ -1,21 +1,18 @@
|
||||
import { hostname, platform } from "node:os";
|
||||
import { getOrchestratorDir, getSocketPath } from "./config.ts";
|
||||
import { loadMachine, saveMachine } from "./storage.ts";
|
||||
import type { InstanceRecord, MachineRecord } from "./types.ts";
|
||||
import type { InstanceRecord, MachineRecord, RadiusRegistration } from "./types.ts";
|
||||
|
||||
const DEFAULT_RADIUS_URL = "https://radius.pi.dev/";
|
||||
const DEFAULT_ORCHESTRATOR_BASE_PATH = "/v1/";
|
||||
const ORCHESTRATOR_VERSION = "0.79.6";
|
||||
|
||||
interface RegisterMachineResponse {
|
||||
interface RegisterMachineResponse extends RadiusRegistration {
|
||||
id: string;
|
||||
heartbeatIntervalMs: number;
|
||||
expiresInMs: number;
|
||||
}
|
||||
|
||||
interface RegisterPiResponse {
|
||||
interface RegisterPiResponse extends RadiusRegistration {
|
||||
id: string;
|
||||
heartbeatIntervalMs: number;
|
||||
expiresInMs: number;
|
||||
}
|
||||
|
||||
async function post<T>(path: string, body: unknown): Promise<T> {
|
||||
@@ -35,8 +32,8 @@ async function post<T>(path: string, body: unknown): Promise<T> {
|
||||
return (await response.json()) as T;
|
||||
}
|
||||
|
||||
function maybePost(path: string, body: unknown): Promise<Response> {
|
||||
return fetch(new URL(path, getRadiusOrchestratorBaseUrl()), {
|
||||
async function maybePost(path: string, body: unknown): Promise<void> {
|
||||
const response = await fetch(new URL(path, getRadiusOrchestratorBaseUrl()), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${getRadiusApiKey()}`,
|
||||
@@ -44,6 +41,9 @@ function maybePost(path: string, body: unknown): Promise<Response> {
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Radius request failed: ${response.status} ${await response.text()}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function getRadiusUrl(): string {
|
||||
@@ -72,7 +72,8 @@ export function isRadiusEnabled(): boolean {
|
||||
}
|
||||
|
||||
export class RadiusPresence {
|
||||
private heartbeatTimer?: NodeJS.Timeout;
|
||||
private machineHeartbeatTimer?: NodeJS.Timeout;
|
||||
private readonly piHeartbeatTimers = new Map<string, NodeJS.Timeout>();
|
||||
private machine?: MachineRecord;
|
||||
|
||||
async start(label?: string): Promise<MachineRecord | undefined> {
|
||||
@@ -80,39 +81,44 @@ export class RadiusPresence {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const registered = await post<RegisterMachineResponse>("/v1/machines/register", {
|
||||
const existingMachine = loadMachine();
|
||||
const registered = await post<RegisterMachineResponse>("machines/register", {
|
||||
machineId: existingMachine?.id,
|
||||
label,
|
||||
hostname: hostname(),
|
||||
platform: platform(),
|
||||
arch: process.arch,
|
||||
version: "0.79.6",
|
||||
version: ORCHESTRATOR_VERSION,
|
||||
capabilities: { spawn: true, relay: false, iroh: false },
|
||||
});
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const timestamp = new Date().toISOString();
|
||||
this.machine = {
|
||||
id: registered.id,
|
||||
createdAt: now,
|
||||
lastSeenAt: now,
|
||||
createdAt: existingMachine?.createdAt ?? timestamp,
|
||||
lastSeenAt: timestamp,
|
||||
label,
|
||||
};
|
||||
saveMachine(this.machine);
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
this.machineHeartbeatTimer = setInterval(() => {
|
||||
void this.heartbeatMachine();
|
||||
}, registered.heartbeatIntervalMs);
|
||||
return this.machine;
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = undefined;
|
||||
if (this.machineHeartbeatTimer) {
|
||||
clearInterval(this.machineHeartbeatTimer);
|
||||
this.machineHeartbeatTimer = undefined;
|
||||
}
|
||||
for (const [instanceId, timer] of this.piHeartbeatTimers) {
|
||||
clearInterval(timer);
|
||||
this.piHeartbeatTimers.delete(instanceId);
|
||||
}
|
||||
if (!this.machine || !isRadiusEnabled()) {
|
||||
return;
|
||||
}
|
||||
await maybePost(`/v1/machines/${this.machine.id}/disconnect`, {});
|
||||
this.machine = undefined;
|
||||
await maybePost(`machines/${this.machine.id}/disconnect`, {});
|
||||
}
|
||||
|
||||
async registerPi(instance: InstanceRecord): Promise<InstanceRecord> {
|
||||
@@ -123,7 +129,7 @@ export class RadiusPresence {
|
||||
if (!machine) {
|
||||
throw new Error("No registered machine available for Pi registration");
|
||||
}
|
||||
const registered = await post<RegisterPiResponse>("/v1/pis/register", {
|
||||
const registered = await post<RegisterPiResponse>("pis/register", {
|
||||
machineId: machine.id,
|
||||
label: instance.label,
|
||||
cwd: instance.cwd,
|
||||
@@ -133,25 +139,49 @@ export class RadiusPresence {
|
||||
capabilities: { rpc: true, relay: false, iroh: false },
|
||||
sessionId: instance.sessionId,
|
||||
});
|
||||
this.startPiHeartbeat(instance.id, registered.heartbeatIntervalMs, registered.id);
|
||||
return { ...instance, radiusPiId: registered.id };
|
||||
}
|
||||
|
||||
async disconnectPi(instance: InstanceRecord): Promise<void> {
|
||||
const timer = this.piHeartbeatTimers.get(instance.id);
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
this.piHeartbeatTimers.delete(instance.id);
|
||||
}
|
||||
if (!isRadiusEnabled() || !instance.radiusPiId) {
|
||||
return;
|
||||
}
|
||||
await maybePost(`/v1/pis/${instance.radiusPiId}/disconnect`, {});
|
||||
await maybePost(`pis/${instance.radiusPiId}/disconnect`, {});
|
||||
}
|
||||
|
||||
private startPiHeartbeat(instanceId: string, intervalMs: number, radiusPiId: string): void {
|
||||
const existingTimer = this.piHeartbeatTimers.get(instanceId);
|
||||
if (existingTimer) {
|
||||
clearInterval(existingTimer);
|
||||
}
|
||||
const timer = setInterval(() => {
|
||||
void this.heartbeatPi(radiusPiId);
|
||||
}, intervalMs);
|
||||
this.piHeartbeatTimers.set(instanceId, timer);
|
||||
}
|
||||
|
||||
private async heartbeatMachine(): Promise<void> {
|
||||
if (!this.machine || !isRadiusEnabled()) {
|
||||
return;
|
||||
}
|
||||
await maybePost(`/v1/machines/${this.machine.id}/heartbeat`, {
|
||||
await maybePost(`machines/${this.machine.id}/heartbeat`, {
|
||||
cwd: getOrchestratorDir(),
|
||||
socketPath: getSocketPath(),
|
||||
});
|
||||
}
|
||||
|
||||
private async heartbeatPi(radiusPiId: string): Promise<void> {
|
||||
if (!isRadiusEnabled()) {
|
||||
return;
|
||||
}
|
||||
await maybePost(`pis/${radiusPiId}/heartbeat`, {});
|
||||
}
|
||||
}
|
||||
|
||||
export const radiusPresence = new RadiusPresence();
|
||||
|
||||
Reference in New Issue
Block a user