fix(bonjour): default mdns host to system hostname

This commit is contained in:
Peter Steinberger
2026-04-27 11:35:11 +01:00
parent 3a73826e28
commit fa468d0c2d
5 changed files with 80 additions and 3 deletions

View File

@@ -704,11 +704,57 @@ describe("gateway bonjour advertiser", () => {
});
const [gatewayCall] = createService.mock.calls as Array<[ServiceCall]>;
expect(gatewayCall?.[0]?.name).toBe("openclaw (OpenClaw)");
expect(gatewayCall?.[0]?.name).toBe("Mac (OpenClaw)");
expect(gatewayCall?.[0]?.domain).toBe("local");
expect(gatewayCall?.[0]?.hostname).toBe("Mac");
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.lanHost).toBe("Mac.local");
await started.stop();
});
it("falls back to openclaw when system hostname is invalid for DNS", async () => {
// Allow advertiser to run in unit tests.
delete process.env.VITEST;
process.env.NODE_ENV = "development";
delete process.env.OPENCLAW_MDNS_HOSTNAME;
vi.spyOn(os, "hostname").mockReturnValue("My_Lobster Host");
const destroy = vi.fn().mockResolvedValue(undefined);
const advertise = vi.fn().mockResolvedValue(undefined);
mockCiaoService({ advertise, destroy });
const started = await startAdvertiser({
gatewayPort: 18789,
sshPort: 2222,
});
const [gatewayCall] = createService.mock.calls as Array<[ServiceCall]>;
expect(gatewayCall?.[0]?.hostname).toBe("openclaw");
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.lanHost).toBe("openclaw.local");
await started.stop();
});
it("uses system hostname when OPENCLAW_MDNS_HOSTNAME is unset", async () => {
// Allow advertiser to run in unit tests.
delete process.env.VITEST;
process.env.NODE_ENV = "development";
delete process.env.OPENCLAW_MDNS_HOSTNAME;
vi.spyOn(os, "hostname").mockReturnValue("Lobster");
const destroy = vi.fn().mockResolvedValue(undefined);
const advertise = vi.fn().mockResolvedValue(undefined);
mockCiaoService({ advertise, destroy });
const started = await startAdvertiser({
gatewayPort: 18789,
sshPort: 2222,
});
const [gatewayCall] = createService.mock.calls as Array<[ServiceCall]>;
expect(gatewayCall?.[0]?.hostname).toBe("Lobster");
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.lanHost).toBe("Lobster.local");
await started.stop();
});
});

View File

@@ -1,6 +1,7 @@
import type { ChildProcess } from "node:child_process";
import fs from "node:fs";
import { createRequire } from "node:module";
import os from "node:os";
import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry";
import { isTruthyEnvValue } from "openclaw/plugin-sdk/runtime-env";
import { classifyCiaoProcessError, type CiaoProcessErrorClassification } from "./ciao.js";
@@ -160,6 +161,28 @@ function isDisabledByEnv() {
return false;
}
function resolveSystemMdnsHostname(): string | null {
let raw: string;
try {
raw = os.hostname();
} catch {
return null;
}
const trimmed = raw.trim();
if (!trimmed) {
return null;
}
const firstLabel =
trimmed
.replace(/\.local$/i, "")
.split(".")[0]
?.trim() ?? "";
if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/.test(firstLabel)) {
return null;
}
return firstLabel;
}
function safeServiceName(name: string) {
const trimmed = name.trim();
return trimmed.length > 0 ? trimmed : "OpenClaw";
@@ -328,7 +351,8 @@ export async function startGatewayBonjourAdvertiser(
cleanupUnhandledRejection = deps.registerUnhandledRejectionHandler?.(handleCiaoProcessError);
cleanupUncaughtException = deps.registerUncaughtExceptionHandler?.(handleCiaoProcessError);
const hostnameRaw = process.env.OPENCLAW_MDNS_HOSTNAME?.trim() || "openclaw";
const hostnameRaw =
process.env.OPENCLAW_MDNS_HOSTNAME?.trim() || resolveSystemMdnsHostname() || "openclaw";
const hostname =
hostnameRaw
.replace(/\.local$/i, "")