refactor: move daemon registry into host-runtime store

This commit is contained in:
Mohamed Boudra
2026-03-12 19:02:06 +07:00
parent 9f089be946
commit 554017b0b8
36 changed files with 1487 additions and 1194 deletions
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { type HostConnection } from "@/contexts/daemon-registry-context";
import type { HostConnection } from "@/types/host-connection";
import {
selectBestConnection,
type ConnectionCandidate,
@@ -1,4 +1,4 @@
import type { HostConnection } from "@/contexts/daemon-registry-context";
import type { HostConnection } from "@/types/host-connection";
export type ConnectionCandidate = {
connectionId: string;
@@ -0,0 +1,55 @@
import { getTauri } from "@/utils/tauri";
let attached = false;
export function attachConsole(): void {
if (attached || !getTauri()) {
return;
}
attached = true;
const originalLog = console.log;
const originalInfo = console.info;
const originalWarn = console.warn;
const originalError = console.error;
const originalDebug = console.debug;
function formatArgs(args: unknown[]): string {
return args
.map((a) =>
typeof a === "string" ? a : JSON.stringify(a, null, 0) ?? String(a)
)
.join(" ");
}
function forwardToRust(level: number, args: unknown[]): void {
const invoke = getTauri()?.core?.invoke;
if (typeof invoke !== "function") return;
try {
void invoke("webview_log", { level, message: formatArgs(args) });
} catch {
// silently ignore IPC errors
}
}
console.log = (...args: unknown[]) => {
originalLog.apply(console, args);
forwardToRust(0, args); // debug
};
console.info = (...args: unknown[]) => {
originalInfo.apply(console, args);
forwardToRust(1, args); // info
};
console.warn = (...args: unknown[]) => {
originalWarn.apply(console, args);
forwardToRust(2, args); // warn
};
console.error = (...args: unknown[]) => {
originalError.apply(console, args);
forwardToRust(3, args); // error
};
console.debug = (...args: unknown[]) => {
originalDebug.apply(console, args);
forwardToRust(0, args); // debug
};
}
@@ -60,40 +60,44 @@ vi.mock("./client-id", () => ({
getOrCreateClientId: clientIdMock.getOrCreateClientId,
}));
describe("test-daemon-connection probe client identity", () => {
describe("test-daemon-connection connectToDaemon", () => {
beforeEach(() => {
daemonClientMock.createdConfigs.length = 0;
clientIdMock.getOrCreateClientId.mockClear();
});
it("reuses the app clientId for direct latency probes", async () => {
it("reuses the app clientId for direct connections", async () => {
const mod = await import("./test-daemon-connection");
await mod.measureConnectionLatency({
id: "direct:lan:6767",
type: "directTcp",
endpoint: "lan:6767",
});
await mod.measureConnectionLatency({
const first = await mod.connectToDaemon({
id: "direct:lan:6767",
type: "directTcp",
endpoint: "lan:6767",
});
await first.client.close();
const [first, second] = daemonClientMock.createdConfigs;
expect(first?.clientId).toBe("cid_shared_probe_test");
expect(second?.clientId).toBe("cid_shared_probe_test");
const second = await mod.connectToDaemon({
id: "direct:lan:6767",
type: "directTcp",
endpoint: "lan:6767",
});
await second.client.close();
const [firstConfig, secondConfig] = daemonClientMock.createdConfigs;
expect(firstConfig?.clientId).toBe("cid_shared_probe_test");
expect(secondConfig?.clientId).toBe("cid_shared_probe_test");
expect(clientIdMock.getOrCreateClientId).toHaveBeenCalledTimes(2);
});
it("encodes the local socket target into the probe client config", async () => {
it("encodes the local socket target into the client config", async () => {
const mod = await import("./test-daemon-connection");
await mod.measureConnectionLatency({
const result = await mod.connectToDaemon({
id: "socket:/tmp/paseo.sock",
type: "directSocket",
path: "/tmp/paseo.sock",
});
await result.client.close();
expect(daemonClientMock.createdConfigs[0]?.url).toBe(
"paseo+local://socket?path=%2Ftmp%2Fpaseo.sock"
@@ -1,6 +1,6 @@
import { DaemonClient } from "@server/client/daemon-client";
import type { DaemonClientConfig } from "@server/client/daemon-client";
import type { HostConnection } from "@/contexts/daemon-registry-context";
import type { HostConnection } from "@/types/host-connection";
import { getOrCreateClientId } from "./client-id";
import { buildDaemonWebSocketUrl, buildRelayWebSocketUrl } from "./daemon-endpoints";
import {
@@ -45,7 +45,7 @@ export class DaemonConnectionTestError extends Error {
}
}
async function buildClientConfig(
export async function buildClientConfig(
connection: HostConnection,
serverId?: string
): Promise<DaemonClientConfig> {
@@ -96,7 +96,7 @@ async function buildClientConfig(
};
}
function connectAndProbe(
export function connectAndProbe(
config: DaemonClientConfig,
timeoutMs: number,
): Promise<{ client: DaemonClient; serverId: string; hostname: string | null }> {
@@ -152,26 +152,10 @@ function resolveTimeout(connection: HostConnection, options?: ProbeOptions): num
return connection.type === "relay" ? 10_000 : 6_000;
}
export async function probeConnection(
export async function connectToDaemon(
connection: HostConnection,
options?: ProbeOptions,
): Promise<{ serverId: string; hostname: string | null }> {
): Promise<{ client: DaemonClient; serverId: string; hostname: string | null }> {
const config = await buildClientConfig(connection, options?.serverId);
const { client, serverId, hostname } = await connectAndProbe(config, resolveTimeout(connection, options));
await client.close().catch(() => undefined);
return { serverId, hostname };
}
export async function measureConnectionLatency(
connection: HostConnection,
options?: ProbeOptions,
): Promise<number> {
const config = await buildClientConfig(connection, options?.serverId);
const { client } = await connectAndProbe(config, resolveTimeout(connection, options));
try {
const { rttMs } = await client.ping({ timeoutMs: 5000 });
return rttMs;
} finally {
await client.close().catch(() => undefined);
}
return connectAndProbe(config, resolveTimeout(connection, options));
}