feat: add machine federation

This commit is contained in:
Marc Kassubeck
2026-05-26 13:07:31 +02:00
parent b5f8810eda
commit a142f5ed40
29 changed files with 852 additions and 1150 deletions
@@ -12,6 +12,7 @@ export class MachineController {
const machines = await api.machines();
const selectedMachine = machines.find((machine) => machine.id === (routeMachineId ?? "local")) ?? machines.find((machine) => machine.id === "local") ?? machines[0];
this.setState({ machines, selectedMachine });
void this.refreshMachineHealthFor(machines);
} catch (error) {
this.setState({ error: String(error) });
} finally {
@@ -33,9 +34,61 @@ export class MachineController {
messagePageTotal: 0,
status: undefined,
activity: undefined,
sessionStatuses: {},
sessionActivities: {},
workspaceActivities: {},
workspacesByProjectId: {},
...resetWorkspaceScopedState(),
});
if (options.updateUrl !== false) this.updateUrl();
await this.projects.loadProjects();
void this.refreshMachineHealth(machine.id);
}
async addMachine(input: { name: string; baseUrl: string; token?: string }): Promise<void> {
this.setState({ error: "" });
try {
const machine = await api.addMachine(input);
this.setState({ machines: [...this.getState().machines.filter((candidate) => candidate.id !== machine.id), machine] });
await this.selectMachine(machine);
} catch (error) {
this.setState({ error: String(error) });
}
}
async deleteMachine(machine: Machine | undefined = this.getState().selectedMachine): Promise<void> {
if (machine === undefined) return;
if (machine.kind === "local") {
this.setState({ error: "The local machine cannot be removed." });
return;
}
try {
await api.deleteMachine(machine.id);
const machines = this.getState().machines.filter((candidate) => candidate.id !== machine.id);
const local = machines.find((candidate) => candidate.id === "local") ?? machines[0];
this.setState({ machines, machineStatuses: omitKey(this.getState().machineStatuses, machine.id) });
if (this.getState().selectedMachine?.id === machine.id && local !== undefined) await this.selectMachine(local);
} catch (error) {
this.setState({ error: String(error) });
}
}
async refreshMachineHealth(machineId = this.getState().selectedMachine?.id ?? "local"): Promise<void> {
try {
const health = await api.health(machineId);
this.setState({ machineStatuses: { ...this.getState().machineStatuses, [health.machineId]: health } });
} catch (error) {
this.setState({ error: String(error) });
}
}
private async refreshMachineHealthFor(machines: Machine[]): Promise<void> {
const results = await Promise.allSettled(machines.map((machine) => api.health(machine.id)));
const health = Object.fromEntries(results.flatMap((result) => result.status === "fulfilled" ? [[result.value.machineId, result.value] as const] : []));
if (Object.keys(health).length > 0) this.setState({ machineStatuses: { ...this.getState().machineStatuses, ...health } });
}
}
function omitKey<T>(record: Record<string, T>, keyToOmit: string): Record<string, T> {
return Object.fromEntries(Object.entries(record).filter(([key]) => key !== keyToOmit));
}