import type { CliDeps } from "../cli/deps.js"; import { retryAsync } from "../infra/retry.js"; import type { RuntimeEnv } from "../runtime.js"; import { upCommand } from "./up.js"; export async function webhookCommand( opts: { port: string; path: string; reply?: string; verbose?: boolean; yes?: boolean; ingress?: "tailscale" | "none"; dryRun?: boolean; }, deps: CliDeps, runtime: RuntimeEnv, ) { const port = Number.parseInt(opts.port, 10); if (Number.isNaN(port) || port <= 0 || port >= 65536) { throw new Error("Port must be between 1 and 65535"); } const ingress = opts.ingress ?? "tailscale"; // Tailscale ingress: reuse the `up` flow (Funnel + Twilio webhook update). if (ingress === "tailscale") { const result = await upCommand( { port: opts.port, path: opts.path, verbose: opts.verbose, yes: opts.yes, dryRun: opts.dryRun, }, deps, runtime, ); return result.server; } // Local-only webhook (no ingress / no Twilio update). await deps.ensurePortAvailable(port); if (opts.reply === "dry-run" || opts.dryRun) { runtime.log( `[dry-run] would start webhook on port ${port} path ${opts.path}`, ); return undefined; } const server = await retryAsync( () => deps.startWebhook( port, opts.path, opts.reply, Boolean(opts.verbose), runtime, ), 3, 300, ); return server; }