/* RFC-0752: subdomain.list command handler — lists all subdomains in a zone by cross-referencing DNS records with Workers routes. Do not validate correctness — that is subdomain.validate's responsibility. RFC-0752: initial subdomain.list command handler. RFC-1249: wire werkstatt.commands.validate into packages.check + retire CMD-OUTPUT debt */ import type { KernelCommandInput, KernelCommandResult, KernelRuntimeContext, } from "@warpgogol/werkstatt-engine/kernel"; import { discoverSystems } from "../sternsystem/registry-io.ts"; import { listDnsRecords, listWorkersRoutes } from "../leitstand/adapters/cloudflare-api.ts"; import { flagString, resolveZoneId, resolveSubdomainEnv } from "./subdomain-helpers.ts"; export interface SubdomainListEntry { domain: string; dnsRecord: { exists: boolean; id: string | null; type: string | null; content: string | null; proxied: boolean | null; }; workersRoute: { exists: boolean; id: string | null; pattern: string | null; script: string | null; }; } export interface SubdomainListResult { command: "subdomain.list"; zone: string; subdomains: SubdomainListEntry[]; } export async function runSubdomainList( input: KernelCommandInput, context: KernelRuntimeContext, ): Promise> { const { workspaceRoot } = context; const zoneDomain = flagString(input, "zone"); if (!zoneDomain) throw new Error("[subdomain.list] --zone is required"); const { systems } = await discoverSystems(workspaceRoot); const zoneId = resolveZoneId(systems, zoneDomain); const env = await resolveSubdomainEnv(); const apiToken = env["CLOUDFLARE_API_TOKEN"]; if (!apiToken) { throw new Error( "[subdomain.list] CLOUDFLARE_API_TOKEN is not set. " + "Set it in the environment or .env file.", ); } const dnsRecords = await listDnsRecords(zoneId, apiToken); const routes = await listWorkersRoutes(zoneId, apiToken); const routeByPattern = new Map(); for (const route of routes) { const domain = route.pattern.replace(/\/\*$/, ""); routeByPattern.set(domain, { id: route.id, script: route.script }); } const subdomains: SubdomainListEntry[] = []; for (const dns of dnsRecords) { const routeEntry = routeByPattern.get(dns.name); subdomains.push({ domain: dns.name, dnsRecord: { exists: true, id: dns.id, type: dns.type, content: dns.content, proxied: dns.proxied, }, workersRoute: routeEntry ? { exists: true, id: routeEntry.id, pattern: `${dns.name}/*`, script: routeEntry.script, } : { exists: false, id: null, pattern: null, script: null, }, }); } for (const [domain, route] of routeByPattern) { const hasDns = dnsRecords.some((d) => d.name === domain); if (!hasDns) { subdomains.push({ domain, dnsRecord: { exists: false, id: null, type: null, content: null, proxied: null, }, workersRoute: { exists: true, id: route.id, pattern: `${domain}/*`, script: route.script, }, }); } } subdomains.sort((a, b) => a.domain.localeCompare(b.domain)); return { data: { command: "subdomain.list", zone: zoneDomain, subdomains, }, exitCode: 0, summary: `[subdomain.list] Zone '${zoneDomain}': ${subdomains.length} subdomain(s) found`, nextSteps: [], }; }