import { z } from "zod" ;
import { safeParseJsonWithSchema } from "../utils/zod-parse.js" ;
export type TailscaleStatusCommandResult = {
code: number | null ;
stdout: string;
};
export type TailscaleStatusCommandRunner = (
argv: string[],
opts: { timeoutMs: number },
) => Promise<TailscaleStatusCommandResult>;
const TAILSCALE_STATUS_COMMAND_CANDIDATES = [
"tailscale" ,
"/Applications/Tailscale.app/Contents/MacOS/Tailscale" ,
];
const TailscaleStatusSchema = z.object({
Self: z
.object({
DNSName: z.string().optional(),
TailscaleIPs: z.array(z.string()).optional(),
})
.optional(),
});
function parsePossiblyNoisyStatus(raw: string): z.infer<typeof TailscaleStatusSchema> | null {
const start = raw.indexOf("{" );
const end = raw.lastIndexOf("}" );
if (start === -1 || end <= start) {
return null ;
}
return safeParseJsonWithSchema(TailscaleStatusSchema, raw.slice(start, end + 1 ));
}
function extractTailnetHostFromStatusJson(raw: string): string | null {
const parsed = parsePossiblyNoisyStatus(raw);
const dns = parsed?.Self?.DNSName;
if (dns && dns.length > 0 ) {
return dns.replace(/\.$/, "" );
}
const ips = parsed?.Self?.TailscaleIPs ?? [];
return ips.length > 0 ? (ips[0 ] ?? null ) : null ;
}
export async function resolveTailnetHostWithRunner(
runCommandWithTimeout?: TailscaleStatusCommandRunner,
): Promise<string | null > {
if (!runCommandWithTimeout) {
return null ;
}
for (const candidate of TAILSCALE_STATUS_COMMAND_CANDIDATES) {
try {
const result = await runCommandWithTimeout([candidate, "status" , "--json" ], {
timeoutMs: 5000 ,
});
if (result.code !== 0 ) {
continue ;
}
const raw = result.stdout.trim();
if (!raw) {
continue ;
}
const host = extractTailnetHostFromStatusJson(raw);
if (host) {
return host;
}
} catch {
continue ;
}
}
return null ;
}
Messung V0.5 in Prozent C=99 H=97 G=97
¤ Dauer der Verarbeitung: 0.2 Sekunden
¤
*© Formatika GbR, Deutschland