/** * RemoteOpenTargets - resolves the SSH hostnames this environment advertises * for remote open-in-editor deep links (`vscode://vscode-remote/ssh-remote+…`). * * The server can only check itself: sshd listening locally, tailscaled * reporting a MagicDNS name, and the machine hostname for mDNS. Whether a * given name resolves from the viewer's machine is inherently client-side. * Targets are ordered most-reachable first (tailnet name works from anywhere * on the tailnet; `.local` only on the same LAN). */ import { type RemoteOpenTarget } from "@t3tools/contracts"; import { HostProcessHostname } from "@t3tools/shared/hostProcess"; import * as NetService from "@t3tools/shared/Net"; import { readTailscaleStatus } from "@t3tools/tailscale"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; const SSH_PORT = 22; export class RemoteOpenTargets extends Context.Service< RemoteOpenTargets, { readonly resolveTargets: () => Effect.Effect>; } >()("t3/environment/RemoteOpenTargets") {} /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.gen(function* () { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; const net = yield* NetService.NetService; const resolveTargets = Effect.gen(function* () { // No local sshd means no name can work; advertise nothing so clients // render a clear "no SSH route" state instead of links that hang. // Check both loopback families: sshd can be bound IPv6-only. const sshdListening = yield* Effect.zipWith( net.hasListenerOnHost(SSH_PORT, "127.0.0.1"), net.hasListenerOnHost(SSH_PORT, "::1"), (ipv4, ipv6) => ipv4 || ipv6, ); if (!sshdListening) { return []; } const targets: Array = []; // Tailscale absent or down is the common case, not an error. const magicDnsName = yield* readTailscaleStatus.pipe( Effect.map((status) => status.magicDnsName), Effect.orElseSucceed(() => null), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), ); if (magicDnsName !== null) { targets.push({ kind: "tailscale", host: magicDnsName }); } // os.hostname() may already be an FQDN (macOS often reports // "Name.local"); mDNS names are always `.local`. const hostname = yield* HostProcessHostname; const shortHostname = hostname.split(".")[0]?.trim(); if (shortHostname !== undefined && shortHostname.length > 0) { targets.push({ kind: "mdns", host: `${shortHostname}.local` }); } return targets; }); return RemoteOpenTargets.of({ resolveTargets: () => resolveTargets }); }); export const layer = Layer.effect(RemoteOpenTargets, make);