{"version":3,"sources":["../src/cli/utils/governance/resolveEndpoint.ts"],"sourcesContent":["/**\n * Single source of truth for resolving the LangWatch control-plane URL\n * across the CLI. Replaces the previous three drifted readers\n * (`endpoint.ts:getEndpoint`, `governance/config.ts:defaults`, and the\n * inline `process.env.LANGWATCH_ENDPOINT ?? ...` literal in\n * `commands/status.ts`) so every command sees the same value for the\n * same inputs.\n *\n * Priority (highest wins):\n *   1. opts.flag           — per-command override (e.g. `langwatch login --endpoint <url>`)\n *   2. LANGWATCH_ENDPOINT  — env var (CI / scripts)\n *   3. persisted config    — `~/.langwatch/config.json:control_plane_url` (daily driver)\n *   4. DEFAULT_ENDPOINT    — `https://app.langwatch.ai` (cloud default)\n *\n * Spec: specs/ai-governance/cli-onboarding/login-unified.feature\n */\n\nimport { DEFAULT_ENDPOINT } from \"@/internal/constants\";\nimport { loadConfig } from \"./config\";\n\nexport interface ResolveEndpointOptions {\n  /** Per-command override flag value (e.g. `--endpoint`). NULL/empty ignored. */\n  flag?: string | null;\n  /** Optional pre-loaded config to reuse (skips disk read). */\n  cfg?: ReturnType<typeof loadConfig>;\n}\n\nexport interface ResolvedEndpoint {\n  /** The resolved URL with trailing slashes stripped. */\n  url: string;\n  /** Where the value came from — useful for `langwatch config list`. */\n  source: \"flag\" | \"env\" | \"config\" | \"default\";\n}\n\n/**\n * Resolve the control-plane endpoint per the documented priority order.\n *\n * Returns both the resolved URL and the source that won, so\n * `langwatch config list` can show the user where each value came from.\n */\nexport function resolveControlPlaneEndpoint(\n  opts: ResolveEndpointOptions = {},\n): ResolvedEndpoint {\n  if (opts.flag) {\n    return { url: stripTrailingSlash(opts.flag), source: \"flag\" };\n  }\n  const env = process.env.LANGWATCH_ENDPOINT;\n  if (env && env.trim() !== \"\") {\n    return { url: stripTrailingSlash(env), source: \"env\" };\n  }\n  // Reading the config involves disk I/O; only do it if we need to.\n  // Callers that already have a cfg in scope can pass it in to skip.\n  let cfg = opts.cfg;\n  if (cfg === undefined) {\n    try {\n      cfg = loadConfig();\n    } catch {\n      cfg = undefined;\n    }\n  }\n  // The persisted control_plane_url is populated EITHER by a prior\n  // `langwatch login --device` (snapshot of env at save time) OR by an\n  // explicit `langwatch config set endpoint <url>`. If it differs from\n  // the hardcoded default, treat it as a user choice.\n  const persisted = cfg?.control_plane_url;\n  if (persisted && persisted !== DEFAULT_ENDPOINT) {\n    return { url: stripTrailingSlash(persisted), source: \"config\" };\n  }\n  return { url: stripTrailingSlash(DEFAULT_ENDPOINT), source: \"default\" };\n}\n\n/**\n * Convenience for callers that just want the URL string and don't need\n * the source-attribution. Equivalent to\n * `resolveControlPlaneEndpoint(opts).url`.\n */\nexport function resolveControlPlaneUrl(\n  opts: ResolveEndpointOptions = {},\n): string {\n  return resolveControlPlaneEndpoint(opts).url;\n}\n\nfunction stripTrailingSlash(url: string): string {\n  return url.replace(/\\/+$/, \"\");\n}\n"],"mappings":";;;;;;;;AAwCO,SAAS,4BACd,OAA+B,CAAC,GACd;AAClB,MAAI,KAAK,MAAM;AACb,WAAO,EAAE,KAAK,mBAAmB,KAAK,IAAI,GAAG,QAAQ,OAAO;AAAA,EAC9D;AACA,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,OAAO,IAAI,KAAK,MAAM,IAAI;AAC5B,WAAO,EAAE,KAAK,mBAAmB,GAAG,GAAG,QAAQ,MAAM;AAAA,EACvD;AAGA,MAAI,MAAM,KAAK;AACf,MAAI,QAAQ,QAAW;AACrB,QAAI;AACF,YAAM,WAAW;AAAA,IACnB,SAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF;AAKA,QAAM,YAAY,2BAAK;AACvB,MAAI,aAAa,cAAc,kBAAkB;AAC/C,WAAO,EAAE,KAAK,mBAAmB,SAAS,GAAG,QAAQ,SAAS;AAAA,EAChE;AACA,SAAO,EAAE,KAAK,mBAAmB,gBAAgB,GAAG,QAAQ,UAAU;AACxE;AAOO,SAAS,uBACd,OAA+B,CAAC,GACxB;AACR,SAAO,4BAA4B,IAAI,EAAE;AAC3C;AAEA,SAAS,mBAAmB,KAAqB;AAC/C,SAAO,IAAI,QAAQ,QAAQ,EAAE;AAC/B;","names":[]}