UNPKG

@hasna/domains

Version:

Domain portfolio, registrar, marketplace, and DNS management for AI agents — CLI + MCP + HTTP API + SDK, shared account API and PostgreSQL

1,332 lines • 55 kB
// @bun
// ../contracts/dist/client/transport.js
import { isIP } from "net";
import { spawnSync } from "child_process";
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync2 } from "fs";
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
import { hostname as osHostname } from "os";
import { isAbsolute, join as join2 } from "path";
import { createRequire } from "module";
import { fileURLToPath as fileURLToPath2 } from "url";
import { closeSync, fstatSync, lstatSync, openSync, readFileSync, realpathSync, statSync } from "fs";
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
import { basename, dirname, join, relative, sep } from "path";
import { fileURLToPath, pathToFileURL } from "url";
function envToken(name) {
  return name.toUpperCase().replace(/-/g, "_");
}
function clientTransportEnvKeys(name) {
  const envSegment = envToken(name);
  return {
    apiUrlKeys: [`HASNA_${envSegment}_API_URL`, `${envSegment}_API_URL`],
    apiKeyKeys: [`HASNA_${envSegment}_API_KEY`, `${envSegment}_API_KEY`]
  };
}
function credentialOverrideEnvKey(name) {
  return `HASNA_${envToken(name)}_API_KEY_OVERRIDE`;
}
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE";
function credentialPointerEnvKey(name) {
  return `HASNA_${envToken(name)}_API_KEY_REF`;
}
function e(e2, n, r) {
  throw new Error(r ? `No known conditions for "${n}" specifier in "${e2}" package` : `Missing "${n}" specifier in "${e2}" package`);
}
function n(n2, i, o, f) {
  let s, u, l = r(n2, o), c = function(e2) {
    let n3 = new Set(["default", ...e2.conditions || []]);
    return e2.unsafe || n3.add(e2.require ? "require" : "import"), e2.unsafe || n3.add(e2.browser ? "browser" : "node"), n3;
  }(f || {}), a = i[l];
  if (a === undefined) {
    let e2, n3, r, t;
    for (t in i)
      n3 && t.length < n3.length || (t[t.length - 1] === "/" && l.startsWith(t) ? (u = l.substring(t.length), n3 = t) : t.length > 1 && (r = t.indexOf("*", 1), ~r && (e2 = RegExp("^" + t.substring(0, r) + "(.*)" + t.substring(1 + r) + "$").exec(l), e2 && e2[1] && (u = e2[1], n3 = t))));
    a = i[n3];
  }
  return a || e(n2, l), s = t(a, c), s || e(n2, l, 1), u && function(e2, n3) {
    let r, t = 0, i2 = e2.length, o2 = /[*]/g, f2 = /[/]$/;
    for (;t < i2; t++)
      e2[t] = o2.test(r = e2[t]) ? r.replace(o2, n3) : f2.test(r) ? r + n3 : r;
  }(s, u), s;
}
function r(e2, n2, r2) {
  if (e2 === n2 || n2 === ".")
    return ".";
  let t = e2 + "/", i = t.length, o = n2.slice(0, i) === t, f = o ? n2.slice(i) : n2;
  return f[0] === "#" ? f : o || !r2 ? f.slice(0, 2) === "./" ? f : "./" + f : f;
}
function t(e2, n2, r2) {
  if (e2) {
    if (typeof e2 == "string")
      return r2 && r2.add(e2), [e2];
    let i, o;
    if (Array.isArray(e2)) {
      for (o = r2 || new Set, i = 0;i < e2.length; i++)
        t(e2[i], n2, o);
      if (!r2 && o.size)
        return [...o];
    } else
      for (i in e2)
        if (n2.has(i))
          return t(e2[i], n2, r2);
  }
}
function o(e2, r2, t2) {
  let i, o2 = e2.exports;
  if (o2) {
    if (typeof o2 == "string")
      o2 = { ".": o2 };
    else
      for (i in o2) {
        i[0] !== "." && (o2 = { ".": o2 });
        break;
      }
    return n(e2.name, o2, r2 || ".", t2);
  }
}
var PACKAGE = "@hasna/secrets";
var MAX_PACKAGE_BYTES = 1024 * 1024;
function entryPoint(directory) {
  const root = realpathSync(directory);
  const fd = openSync(join(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
  let pkg;
  try {
    const stat = fstatSync(fd);
    if (!stat.isFile() || stat.size > MAX_PACKAGE_BYTES)
      throw new Error("Invalid Secrets package metadata");
    pkg = JSON.parse(readFileSync(fd, "utf8"));
  } finally {
    closeSync(fd);
  }
  if (!pkg || typeof pkg !== "object" || !("name" in pkg) || pkg.name !== PACKAGE) {
    throw new Error("Invalid Secrets package identity");
  }
  const target = o(pkg, ".")?.[0];
  if (typeof target !== "string" || !target.startsWith("./"))
    throw new Error("Missing Secrets import export");
  const parts = target.slice(2).split("/");
  if (/[\\%?#\0]/.test(target) || parts.some((part) => !part || part === "." || part === ".." || part === "node_modules")) {
    throw new Error("Invalid Secrets import export");
  }
  const file = realpathSync(join(root, ...parts));
  const within = relative(root, file);
  if (!within || within === ".." || within.startsWith(`..${sep}`) || !statSync(file).isFile()) {
    throw new Error("Secrets import export escapes its package or is not a file");
  }
  return pathToFileURL(file).href;
}
function resolveInstalledSecrets(parentUrl) {
  let directory = dirname(fileURLToPath(parentUrl));
  for (;; ) {
    if (basename(directory) !== "node_modules") {
      const candidate = join(directory, "node_modules", PACKAGE);
      let absent = false;
      try {
        lstatSync(candidate);
      } catch (error) {
        if (error.code !== "ENOENT")
          throw error;
        absent = true;
      }
      if (!absent)
        return entryPoint(candidate);
    }
    const parent = dirname(directory);
    if (parent === directory)
      throw new Error("Secrets SDK is not installed for this consumer");
    directory = parent;
  }
}

class CredentialResolutionError extends Error {
  appName;
  attempted;
  constructor(appName, message, attempted) {
    super(message);
    this.name = "CredentialResolutionError";
    this.appName = appName;
    this.attempted = attempted;
  }
}

class CredentialFileUnsafeError extends Error {
  path;
  constructor(path, reason) {
    super(`Refusing unsafe credential/config file ${path}: ${reason}.`);
    this.name = "CredentialFileUnsafeError";
    this.path = path;
  }
}
var HASNA_HOME_ENV_KEY = "HASNA_HOME";
var HASNA_CONFIG_HOME_ENV_KEY = "HASNA_CONFIG_HOME";
var KEYCHAIN_STATION_ENV_KEY = "HASNA_STATION";
var HASNA_HOME_DIR = ".hasna";
var CONFIG_SUBDIR = "config";
var CREDENTIALS_FILE = "credentials";
var KEYCHAIN_SECURITY_BIN = "/usr/bin/security";
var KEYCHAIN_SERVICE_PREFIX = "hasna.credentials";
var KEYCHAIN_ITEM_NOT_FOUND_STATUS = 44;
var KEYCHAIN_SPAWN_TIMEOUT_MS = 1e4;
var MAX_CREDENTIAL_FILE_BYTES = 64 * 1024;
var SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
var SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
var ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
var VAULT_POINTER_SHAPE = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-_.]*){2,}$/;
function homeDir(env) {
  const home = env.HOME?.trim();
  return home ? home : null;
}
function absoluteOverride(env, key) {
  const value = env[key]?.trim();
  return value && isAbsolute(value) ? value : null;
}
function hasnaHomeDir(env) {
  const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
  if (override)
    return override;
  const home = homeDir(env);
  return home ? join2(home, HASNA_HOME_DIR) : null;
}
function appConfigDir(name, env) {
  const configRoot = absoluteOverride(env, HASNA_CONFIG_HOME_ENV_KEY);
  if (configRoot)
    return join2(configRoot, name);
  const root = hasnaHomeDir(env);
  return root ? join2(root, name, CONFIG_SUBDIR) : null;
}
function credentialDiskSourceList(name, env, profile = null) {
  if (!SAFE_APP_SLUG.test(name))
    return [];
  const directory = appConfigDir(name, env);
  if (!directory)
    return [];
  const file = profile ? `${CREDENTIALS_FILE}-${profile}` : CREDENTIALS_FILE;
  return [{ path: join2(directory, file), tier: "disk" }];
}
function credentialDiskSources(name, env) {
  return credentialDiskSourceList(name, env, null).map((s) => s.path);
}
function profileDiskSources(name, env, profile) {
  return credentialDiskSourceList(name, env, profile).map((s) => s.path);
}
function parseEnvFile(text) {
  const values = new Map;
  const unusable = new Set;
  for (const rawLine of text.split(/\r?\n/)) {
    const line = rawLine.trim();
    if (line.length === 0 || line.startsWith("#"))
      continue;
    const withoutExport = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
    const equals = withoutExport.indexOf("=");
    if (equals <= 0)
      continue;
    const key = withoutExport.slice(0, equals).trim();
    if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
      continue;
    let value = withoutExport.slice(equals + 1).trim();
    const quote = value[0];
    if (quote === '"' || quote === "'") {
      if (value.length < 2 || !value.endsWith(quote)) {
        unusable.add(key);
        continue;
      }
      value = value.slice(1, -1);
    }
    if (value.trim().length === 0) {
      unusable.add(key);
      continue;
    }
    if (values.has(key) && values.get(key) !== value)
      unusable.add(key);
    values.set(key, value);
  }
  return { values, unusable };
}
function configFileModeAllowed(mode) {
  const permissions = mode & 4095;
  return permissions === 256 || permissions === 384;
}
function configFileReadsCoherent(before, after) {
  return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs;
}
function readAppConfigFile(path) {
  const unsafe = (reason) => {
    throw new CredentialFileUnsafeError(path, reason);
  };
  let fd = -1;
  try {
    fd = openSync2(path, O_RDONLY2 | O_NOFOLLOW2 | O_NONBLOCK2);
  } catch (error) {
    const code = error.code;
    if (code === "ENOENT" || code === "ENOTDIR")
      return null;
    if (code === "ELOOP")
      unsafe("the path is a symlink");
    unsafe(`the path could not be opened (${code ?? "unknown error"})`);
  }
  try {
    const before = fstatSync2(fd);
    if (!before.isFile())
      unsafe("the path is not a regular file");
    if (!configFileModeAllowed(before.mode)) {
      unsafe(`permission mode ${(before.mode & 4095).toString(8).padStart(4, "0")} is not owner-only 0400 or 0600`);
    }
    const uid = process.getuid?.() ?? process.geteuid?.();
    if (uid !== undefined && before.uid !== uid)
      unsafe("the file is not owned by the current user");
    if (before.size > MAX_CREDENTIAL_FILE_BYTES)
      unsafe("the file exceeds the size limit");
    const bytes = readFileSync2(fd);
    const after = fstatSync2(fd);
    if (!configFileReadsCoherent(before, after)) {
      unsafe("the file changed while being read");
    }
    return parseEnvFile(bytes.toString("utf8"));
  } finally {
    if (fd !== -1)
      closeSync2(fd);
  }
}
function readCredentialFile(path, apiKeyKeys, pointerKey) {
  const parsed = readAppConfigFile(path);
  if (!parsed)
    return null;
  for (const key of [...apiKeyKeys, pointerKey]) {
    if (parsed.unusable.has(key)) {
      throw new CredentialFileUnsafeError(path, `${key} is declared but blank or malformed`);
    }
  }
  const values = apiKeyKeys.map((key) => parsed.values.get(key)?.trim()).filter((value) => Boolean(value));
  const pointer = parsed.values.get(pointerKey)?.trim();
  if (pointer !== undefined) {
    if (!VAULT_POINTER_SHAPE.test(pointer))
      throw new CredentialFileUnsafeError(path, `${pointerKey} must name a vault item`);
    if (values.length)
      throw new CredentialFileUnsafeError(path, "a credential file cannot select both a literal key and a vault reference");
    return { apiKey: "", pointerVaultKey: pointer };
  }
  if (new Set(values).size > 1) {
    throw new CredentialFileUnsafeError(path, "credential aliases disagree");
  }
  return values[0] === undefined ? null : { apiKey: values[0] };
}
var CREDENTIAL_SHAPED_KEY = /(?:^|_)(?:API_KEY|KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)(?:_|$)/;
function appConfigDiskValue(name, env, keys, profile = null) {
  if (profile !== null && !SAFE_PROFILE.test(profile)) {
    throw new CredentialResolutionError(name, "The selected configuration profile is invalid.", ["profile"]);
  }
  const wanted = keys.filter((key) => !CREDENTIAL_SHAPED_KEY.test(key));
  if (wanted.length === 0)
    return null;
  for (const path of profileDiskSources(name, env, profile)) {
    const parsed = readAppConfigFile(path);
    if (!parsed)
      continue;
    if (wanted.some((key) => parsed.unusable.has(key))) {
      return { key: wanted.find((key) => parsed.unusable.has(key)), value: "", path, unusable: true };
    }
    const values = wanted.map((key) => parsed.values.get(key)?.trim()).filter((value) => Boolean(value));
    if (new Set(values).size > 1)
      throw new CredentialFileUnsafeError(path, "configuration aliases disagree");
    for (const key of wanted) {
      if (parsed.unusable.has(key))
        return { key, value: "", path, unusable: true };
      const value = parsed.values.get(key)?.trim();
      if (value)
        return { key, value, path };
    }
  }
  return null;
}
function assertUsableCredential(appName, source, value) {
  if (VAULT_POINTER_SHAPE.test(value)) {
    throw new CredentialResolutionError(appName, `The credential from ${source} looks like a secrets-vault pointer (a path-shaped reference like ` + `'namespace/app/live/api_key'). A vault path is NEVER accepted as a literal API key. ` + `Use ${credentialPointerEnvKey(appName)} to resolve the key through the vault, or provide the actual key value.`, [source]);
  }
  if (!ILLEGAL_IN_HEADER_VALUE.test(value))
    return;
  throw new CredentialResolutionError(appName, `The credential from ${source} contains characters that cannot be sent in an HTTP header ` + `(a control character or non-ASCII byte). A file written with CR-only line endings is the usual ` + `cause. Rewrite that credential file with one LF-terminated KEY=value line. ` + `The value is not shown here, and is deliberately never logged.`, [source]);
}
var INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
var CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
function sealCredential(fields) {
  const { apiKey } = fields;
  const visible = {
    tier: fields.tier,
    source: fields.source,
    deliberate: fields.deliberate,
    diskCandidates: Object.freeze([...fields.diskCandidates]),
    warning: fields.warning
  };
  const sealed = { ...visible };
  Object.defineProperty(sealed, "apiKey", {
    value: apiKey,
    enumerable: false,
    writable: false,
    configurable: false
  });
  if (fields.pointerVaultKey !== undefined) {
    Object.defineProperty(sealed, "pointerVaultKey", {
      value: fields.pointerVaultKey,
      enumerable: false,
      writable: false,
      configurable: false
    });
  }
  Object.defineProperty(sealed, INSPECT_CUSTOM, {
    value: () => ({ ...visible, apiKey: "[redacted]" }),
    enumerable: false,
    writable: false,
    configurable: false
  });
  Object.defineProperty(sealed, CREDENTIAL_SEAL, {
    value: true,
    enumerable: false,
    writable: false,
    configurable: false
  });
  return Object.freeze(sealed);
}
function firstEnvValue(env, keys) {
  for (const key of keys) {
    if (!Object.prototype.hasOwnProperty.call(env, key))
      continue;
    const value = env[key]?.trim();
    if (value)
      return { key, value };
  }
  return null;
}
var AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
function isAmbientEnvironment(env) {
  return env === process.env || env[AMBIENT_ENVIRONMENT] === true;
}
function defaultKeychainRunner(argv) {
  const result = spawnSync(KEYCHAIN_SECURITY_BIN, [...argv], {
    encoding: "utf8",
    stdio: ["ignore", "pipe", "pipe"],
    timeout: KEYCHAIN_SPAWN_TIMEOUT_MS
  });
  return {
    status: result.status,
    stdout: result.stdout ?? "",
    stderr: result.error ? result.error.message : result.stderr ?? ""
  };
}
function keychainTierEnabled(env, options) {
  if ((options.platform ?? process.platform) !== "darwin")
    return false;
  if (options.enabled !== undefined)
    return options.enabled;
  return options.run !== undefined || isAmbientEnvironment(env);
}
function keychainAccount(env, options) {
  const station = env[KEYCHAIN_STATION_ENV_KEY]?.trim();
  if (station)
    return station;
  const host = (options.hostname ?? osHostname)().split(".")[0]?.trim() ?? "";
  if (host)
    return host;
  const user = env.USER?.trim();
  return user || null;
}
function keychainFailureHint(text) {
  const line = text.split(/\r?\n/).find((entry) => entry.trim().length > 0)?.trim() ?? "";
  const clean = line.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 200);
  return clean ? `: ${clean}` : "";
}
function readKeychainItem(name, env, kind, options) {
  if (!SAFE_APP_SLUG.test(name) || !keychainTierEnabled(env, options))
    return null;
  const account = keychainAccount(env, options);
  if (!account)
    return null;
  const service = `${KEYCHAIN_SERVICE_PREFIX}.${name}.${kind}`;
  const source = `keychain:${service}@${account}`;
  const run = options.run ?? defaultKeychainRunner;
  let result;
  try {
    result = run(["find-generic-password", "-a", account, "-s", service, "-w"]);
  } catch (error) {
    const reason = keychainFailureHint(error instanceof Error ? error.message : String(error));
    throw new CredentialResolutionError(name, `The Keychain lookup for ${source} could not run${reason}. A Keychain failure is never resolved ` + `around: fix the keychain, or delete the item to fall through to the credential on disk.`, [source]);
  }
  if (result.status === KEYCHAIN_ITEM_NOT_FOUND_STATUS)
    return null;
  if (result.status !== 0) {
    throw new CredentialResolutionError(name, `The Keychain lookup for ${source} failed (security exited ` + `${result.status ?? "without a status"}${keychainFailureHint(result.stderr)}). A Keychain item that ` + `exists but cannot be read is never resolved around: unlock the keychain, run from a session that ` + `may use it, or delete the item to fall through to the credential on disk.`, [source]);
  }
  const value = result.stdout.trim();
  if (!value) {
    throw new CredentialResolutionError(name, `${source} exists but holds an empty value; a declared item never falls through to another ` + `identity. Store a value in it or delete the item.`, [source]);
  }
  return { value, source };
}
function keychainConfigValue(name, env, options = {}) {
  return readKeychainItem(name, env, "api-url", options);
}
function snapshotClientEnvironment(name, env) {
  const keys = clientTransportEnvKeys(name);
  const ambient = isAmbientEnvironment(env);
  const snapshot = Object.create(null);
  for (const key of [
    ...keys.apiUrlKeys,
    ...keys.apiKeyKeys,
    credentialOverrideEnvKey(name),
    credentialPointerEnvKey(name),
    CREDENTIAL_PROFILE_ENV_KEY,
    "HOME",
    HASNA_HOME_ENV_KEY,
    HASNA_CONFIG_HOME_ENV_KEY,
    KEYCHAIN_STATION_ENV_KEY,
    "USER"
  ]) {
    const descriptor = Object.getOwnPropertyDescriptor(env, key);
    if (!descriptor)
      continue;
    if (!("value" in descriptor)) {
      throw new CredentialResolutionError(name, `${key} is accessor-backed; client configuration requires own data properties.`, [key]);
    }
    if (descriptor.value !== undefined && typeof descriptor.value !== "string") {
      throw new CredentialResolutionError(name, `${key} must be a string data property.`, [key]);
    }
    snapshot[key] = descriptor.value;
  }
  if (ambient) {
    Object.defineProperty(snapshot, AMBIENT_ENVIRONMENT, {
      value: true,
      enumerable: false,
      writable: false,
      configurable: false
    });
  }
  return Object.freeze(snapshot);
}
function resolveCredential(name, env, options = {}) {
  env = snapshotClientEnvironment(name, env);
  const { apiKeyKeys } = clientTransportEnvKeys(name);
  const diskPaths = credentialDiskSources(name, env);
  if (options.apiKey !== undefined) {
    const explicitKey = options.apiKey.trim();
    if (!explicitKey) {
      throw new CredentialResolutionError(name, "The explicit apiKey argument is blank; an explicit credential never falls through to another identity.", ["explicit apiKey argument"]);
    }
    assertUsableCredential(name, "the explicit apiKey argument", explicitKey);
    return sealCredential({
      apiKey: explicitKey,
      tier: "argument",
      source: "explicit apiKey argument",
      deliberate: true,
      diskCandidates: diskPaths,
      warning: null
    });
  }
  const overrideKeyName = credentialOverrideEnvKey(name);
  const overrideRaw = Object.prototype.hasOwnProperty.call(env, overrideKeyName) ? env[overrideKeyName] : undefined;
  if (overrideRaw !== undefined) {
    const override = overrideRaw.trim();
    if (!override) {
      throw new CredentialResolutionError(name, `${overrideKeyName} is set but empty. It is a deliberate override, so it is not resolved around: ` + `either give it a real key or unset it to fall back to the credential on disk.`, [overrideKeyName]);
    }
    assertUsableCredential(name, overrideKeyName, override);
    return sealCredential({
      apiKey: override,
      tier: "override",
      source: overrideKeyName,
      deliberate: true,
      diskCandidates: diskPaths,
      warning: null
    });
  }
  const pointerKeyName = credentialPointerEnvKey(name);
  const pointerRaw = Object.prototype.hasOwnProperty.call(env, pointerKeyName) ? env[pointerKeyName] : undefined;
  if (pointerRaw !== undefined) {
    const pointer = pointerRaw.trim();
    if (!pointer) {
      throw new CredentialResolutionError(name, `${pointerKeyName} is set but empty. It is a deliberate vault pointer, so it is not resolved around: ` + `either give it a vault item key or unset it to fall back to the credential on disk.`, [pointerKeyName]);
    }
    if (!VAULT_POINTER_SHAPE.test(pointer)) {
      throw new CredentialResolutionError(name, `${pointerKeyName} must name a vault ITEM KEY (a path-shaped reference like ` + `'namespace/app/live/api_key'), not a credential value. A pointer that carries a literal is refused.`, [pointerKeyName]);
    }
    return sealCredential({
      apiKey: "",
      pointerVaultKey: pointer,
      tier: "pointer",
      source: pointerKeyName,
      deliberate: true,
      diskCandidates: diskPaths,
      warning: null
    });
  }
  if (options.profile !== undefined && !options.profile.trim()) {
    throw new CredentialResolutionError(name, "The explicit profile argument is blank; an explicit identity selection never falls through.", ["explicit profile argument"]);
  }
  const profileRaw = Object.prototype.hasOwnProperty.call(env, CREDENTIAL_PROFILE_ENV_KEY) ? env[CREDENTIAL_PROFILE_ENV_KEY] : undefined;
  if (profileRaw !== undefined && !profileRaw.trim()) {
    throw new CredentialResolutionError(name, `${CREDENTIAL_PROFILE_ENV_KEY} is set but blank.`, [CREDENTIAL_PROFILE_ENV_KEY]);
  }
  const profile = options.profile?.trim() || profileRaw?.trim();
  if (profile) {
    const profileSource = options.profile?.trim() ? "explicit profile argument" : CREDENTIAL_PROFILE_ENV_KEY;
    if (!SAFE_PROFILE.test(profile)) {
      throw new CredentialResolutionError(name, `Profile name from ${profileSource} is not usable in a path. ` + `Use letters, digits, dot, dash, or underscore.`, [profileSource]);
    }
    const paths = profileDiskSources(name, env, profile);
    for (const path of paths) {
      const value = readCredentialFile(path, apiKeyKeys, pointerKeyName);
      if (value) {
        if (!value.pointerVaultKey)
          assertUsableCredential(name, path, value.apiKey);
        return sealCredential({
          ...value,
          tier: value.pointerVaultKey ? "pointer" : "profile",
          source: path,
          deliberate: true,
          diskCandidates: paths,
          warning: null
        });
      }
    }
    throw new CredentialResolutionError(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} or ${pointerKeyName} for '${name}'. ` + `Looked in: ${paths.join(", ") || "<no HOME in this environment>"}. ` + `A profile names WHICH identity to use, so it is never resolved around \u2014 ` + `create the profile's credential file or unset ${CREDENTIAL_PROFILE_ENV_KEY}.`, paths);
  }
  const definedEnvEntries = apiKeyKeys.filter((key) => Object.prototype.hasOwnProperty.call(env, key) && env[key] !== undefined).map((key) => ({ key, value: String(env[key]).trim() }));
  const blankEnv = definedEnvEntries.find((entry) => entry.value.length === 0);
  if (blankEnv) {
    throw new CredentialResolutionError(name, `${blankEnv.key} is set but blank; a declared credential never falls through to another alias or identity.`, [blankEnv.key]);
  }
  if (definedEnvEntries.length > 1 && new Set(definedEnvEntries.map((entry) => entry.value)).size > 1) {
    throw new CredentialResolutionError(name, `${definedEnvEntries.map((entry) => entry.key).join(" and ")} disagree; credential aliases must be identical or only one may be set.`, definedEnvEntries.map((entry) => entry.key));
  }
  const envHit = firstEnvValue(env, apiKeyKeys);
  const keychainHit = readKeychainItem(name, env, "api-key", options.keychain ?? {});
  if (keychainHit) {
    assertUsableCredential(name, keychainHit.source, keychainHit.value);
    const warning = envHit && envHit.value !== keychainHit.value ? `Credential sources disagree for '${name}': ${keychainHit.source} and ${envHit.key} hold ` + `different keys. ${keychainHit.source} wins, because the Keychain is re-read on every call while ` + `an environment variable is a snapshot. Reconcile them \u2014 a rotation that updated only one leaves ` + `the other to fail 401 wherever it is loaded first.` : null;
    return sealCredential({
      apiKey: keychainHit.value,
      tier: "keychain",
      source: keychainHit.source,
      deliberate: false,
      diskCandidates: diskPaths,
      warning
    });
  }
  const diskSourceList = credentialDiskSourceList(name, env, null);
  const diskHits = diskSourceList.map((src) => ({ src, value: readCredentialFile(src.path, apiKeyKeys, pointerKeyName) })).filter((hit) => hit.value !== null);
  if (diskHits.length > 0) {
    const winner = diskHits[0];
    if (winner.value.pointerVaultKey) {
      return sealCredential({
        ...winner.value,
        tier: "pointer",
        source: winner.src.path,
        deliberate: false,
        diskCandidates: diskPaths,
        warning: null
      });
    }
    assertUsableCredential(name, winner.src.path, winner.value.apiKey);
    const divergentSources = [
      ...diskHits.slice(1).filter((hit) => hit.value.apiKey !== winner.value.apiKey || hit.value.pointerVaultKey !== winner.value.pointerVaultKey).map((hit) => hit.src.path),
      ...envHit && envHit.value !== winner.value.apiKey ? [envHit.key] : []
    ];
    const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.src.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.src.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
    return sealCredential({
      apiKey: winner.value.apiKey,
      tier: winner.src.tier,
      source: winner.src.path,
      deliberate: false,
      diskCandidates: diskPaths,
      warning
    });
  }
  if (envHit) {
    assertUsableCredential(name, envHit.key, envHit.value);
    return sealCredential({
      apiKey: envHit.value,
      tier: "env",
      source: envHit.key,
      deliberate: false,
      diskCandidates: diskPaths,
      warning: null
    });
  }
  return null;
}
var requireSecretsSdk = createRequire(import.meta.url);
async function completePointerCredential(name, pointerResolution, env = process.env) {
  const secretsEnv = snapshotClientEnvironment("secrets", env);
  const vaultKey = pointerResolution.pointerVaultKey;
  const pointerEnvKey = pointerResolution.source;
  if (!vaultKey) {
    throw new CredentialResolutionError(name, `Pointer resolution from ${pointerEnvKey} carries no vault item key; this is a defect in the resolver.`, [pointerEnvKey]);
  }
  if (name === "secrets") {
    throw new CredentialResolutionError(name, "The Secrets bootstrap credential cannot reference the same hosted vault; configure an independent bootstrap provider.", [pointerEnvKey]);
  }
  let secretsSdk;
  try {
    const sdkUrl = resolveInstalledSecrets(import.meta.url);
    secretsSdk = requireSecretsSdk(fileURLToPath2(sdkUrl));
  } catch {
    throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets SDK (@hasna/secrets) is not installed ` + `in this process. A vault pointer is TERMINAL: install @hasna/secrets to resolve it, or unset ${pointerEnvKey}.`, [pointerEnvKey]);
  }
  let client;
  try {
    const bootstrap = resolveCredential("secrets", secretsEnv);
    if (!bootstrap || bootstrap.tier === "pointer") {
      throw new CredentialResolutionError("secrets", "The Secrets vault requires an independent, non-reference bootstrap credential.", bootstrap ? [bootstrap.source] : []);
    }
    client = secretsSdk.createSecretsClientFromEnv(secretsEnv);
  } catch {
    throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets client could not be configured from this ` + `environment (the secrets service URL and key env are missing or invalid). A vault pointer is TERMINAL and ` + `never falls through to a literal or disk credential.`, [pointerEnvKey]);
  }
  let secret;
  try {
    secret = await client.getSecret({ key: vaultKey });
  } catch {
    throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the vault could not be reached or the item is ` + `unavailable. A vault pointer is TERMINAL and never falls through to a literal or disk credential.`, [pointerEnvKey]);
  }
  const value = secret.value;
  if (!value) {
    throw new CredentialResolutionError(name, `${pointerEnvKey} resolved vault item '${vaultKey}', but it holds no value. A vault pointer is TERMINAL.`, [pointerEnvKey]);
  }
  assertUsableCredential(name, `${pointerEnvKey} -> vault:${vaultKey}`, value);
  return sealCredential({
    apiKey: value,
    tier: "pointer",
    source: `${pointerEnvKey} -> vault:${vaultKey}`,
    deliberate: pointerResolution.deliberate,
    diskCandidates: pointerResolution.diskCandidates,
    warning: null
  });
}
var DEFAULT_FLEET_GATEWAY_ORIGIN = "https://api.hasna.com";
var DEFAULT_AUTHORITY_SOURCE = "default";
function defaultFleetGatewayBaseUrl(name) {
  return `${DEFAULT_FLEET_GATEWAY_ORIGIN}/${validateAppSlug(name)}`;
}
var ASCII_CONTROL_PATTERN = /[\u0000-\u001f\u007f]/;
var DNS_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
function isValidDnsDomain(value) {
  if (value.length === 0 || value.length > 253 || ASCII_CONTROL_PATTERN.test(value) || /[^\x00-\x7f]/.test(value)) {
    return false;
  }
  return value.split(".").every((label) => label.length <= 63 && !label.startsWith("xn--") && DNS_LABEL_PATTERN.test(label));
}
function validateAppSlug(name) {
  if (name.length > 63 || !DNS_LABEL_PATTERN.test(name)) {
    throw new Error("App name must be one lowercase DNS label.");
  }
  return name;
}
function rawAuthority(value) {
  const match = /^[a-z][a-z0-9+.-]*:\/\//i.exec(value);
  if (!match)
    throw new Error("API URL must be absolute.");
  const afterScheme = value.slice(match[0].length);
  const boundary = afterScheme.search(/[/?#]/);
  const authority = boundary === -1 ? afterScheme : afterScheme.slice(0, boundary);
  if (!authority)
    throw new Error("API URL must include a hostname.");
  return authority;
}
function assertCanonicalPort(port) {
  if (!/^[0-9]+$/.test(port) || port.length > 1 && port.startsWith("0")) {
    throw new Error("API URL authority must contain a canonical port between 1 and 65535.");
  }
  const numericPort = Number(port);
  if (!Number.isSafeInteger(numericPort) || numericPort < 1 || numericPort > 65535) {
    throw new Error("API URL authority must contain a canonical port between 1 and 65535.");
  }
}
function canonicalAuthorityHostname(authority) {
  let rawHostname;
  if (authority.startsWith("[")) {
    const closingBracket = authority.indexOf("]");
    if (closingBracket === -1) {
      throw new Error("API URL authority must contain a canonical hostname.");
    }
    rawHostname = authority.slice(0, closingBracket + 1);
    const portSuffix = authority.slice(closingBracket + 1);
    if (portSuffix) {
      if (!portSuffix.startsWith(":")) {
        throw new Error("API URL authority must contain a canonical hostname and port.");
      }
      assertCanonicalPort(portSuffix.slice(1));
    }
    if (isIP(rawHostname.slice(1, -1)) !== 6) {
      throw new Error("API URL authority must contain a canonical IPv6 literal.");
    }
  } else {
    const firstColon = authority.indexOf(":");
    const lastColon = authority.lastIndexOf(":");
    if (firstColon !== lastColon) {
      throw new Error("IPv6 API URL authorities must use brackets.");
    }
    if (lastColon !== -1) {
      const port = authority.slice(lastColon + 1);
      assertCanonicalPort(port);
      rawHostname = authority.slice(0, lastColon);
    } else {
      rawHostname = authority;
    }
    const ipVersion = isIP(rawHostname);
    const numericAddressParts = rawHostname.split(".");
    const looksLikeNonCanonicalIpv4 = numericAddressParts.every((part) => /^(?:0x[0-9a-f]+|[0-9]+)$/i.test(part));
    if (ipVersion !== 4 && looksLikeNonCanonicalIpv4 || ipVersion !== 4 && !isValidDnsDomain(rawHostname.toLowerCase())) {
      throw new Error("API URL authority must contain a canonical ASCII hostname.");
    }
  }
  return rawHostname.toLowerCase();
}
function isDeliberateLoopbackHttpAuthority(authority) {
  return /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/i.test(authority);
}
function toV1BaseUrl(apiUrl) {
  if (ASCII_CONTROL_PATTERN.test(apiUrl)) {
    throw new Error("API URL must not contain ASCII control characters.");
  }
  const input = apiUrl.trim();
  const authority = rawAuthority(input);
  if (authority.includes("@") || authority.includes("\\") || authority.includes("%") || /[^\x00-\x7f]/.test(authority)) {
    throw new Error("API URL authority must be canonical ASCII without credentials.");
  }
  const canonicalHostname = canonicalAuthorityHostname(authority);
  const url = new URL(input);
  if (url.protocol !== "http:" && url.protocol !== "https:") {
    throw new Error("API URL must use http or https.");
  }
  if (url.username || url.password) {
    throw new Error("API URL must not include credentials.");
  }
  if (!url.hostname || url.hostname.endsWith(".")) {
    throw new Error("API URL must include a canonical hostname.");
  }
  if (url.hostname.toLowerCase() !== canonicalHostname) {
    throw new Error("API URL authority must not rely on parser hostname normalization.");
  }
  if (url.hostname.split(".").some((label) => label.toLowerCase().startsWith("xn--"))) {
    throw new Error("API URL must not use IDN or punycode hostnames.");
  }
  if (url.protocol === "http:" && !isDeliberateLoopbackHttpAuthority(authority)) {
    throw new Error("API URL may use http only for an exact loopback authority.");
  }
  if (url.search || url.hash) {
    throw new Error("API URL must not include a query string or fragment.");
  }
  let path = url.pathname.replace(/\/+$/, "");
  if (path.endsWith("/v1"))
    path = path.slice(0, -"/v1".length);
  url.pathname = `${path}/v1`;
  return url.toString().replace(/\/+$/, "");
}
class ClientTransportConfigurationError extends Error {
  appName;
  sources;
  constructor(appName, message, sources = []) {
    super(message);
    this.name = "ClientTransportConfigurationError";
    this.appName = appName;
    this.sources = Object.freeze([...sources]);
  }
}

class ClientCredentialMissingError extends ClientTransportConfigurationError {
  authorityConfigured;
  constructor(name, message, sources, authorityConfigured) {
    super(name, message, sources);
    this.authorityConfigured = authorityConfigured;
    this.name = "ClientCredentialMissingError";
  }
}
function resolveClientAuthoritySnapshot(name, env = process.env, options = {}) {
  env = snapshotClientEnvironment(name, env);
  const keys = clientTransportEnvKeys(name);
  const definedUrlEntries = keys.apiUrlKeys.filter((key) => Object.prototype.hasOwnProperty.call(env, key) && env[key] !== undefined).map((key) => ({ key, raw: String(env[key]) }));
  const blankUrl = definedUrlEntries.find((entry) => entry.raw.trim().length === 0);
  if (blankUrl) {
    throw new ClientTransportConfigurationError(name, `${blankUrl.key} is set but blank; public clients require an explicit HTTPS API URL and never select local storage.`, [blankUrl.key]);
  }
  const controlledUrl = definedUrlEntries.find((entry) => ASCII_CONTROL_PATTERN.test(entry.raw));
  if (controlledUrl) {
    throw new ClientTransportConfigurationError(name, `${controlledUrl.key} contains ASCII control characters.`, [controlledUrl.key]);
  }
  const usableUrlEntries = definedUrlEntries.map((entry) => ({ key: entry.key, value: entry.raw.trim() }));
  const normalizedAuthority = (entry) => {
    try {
      return toV1BaseUrl(entry.value);
    } catch {
      throw new ClientTransportConfigurationError(name, `Invalid API URL from ${entry.key}; use an absolute HTTPS URL or exact loopback HTTP authority without credentials, query or fragment.`, [entry.key]);
    }
  };
  if (usableUrlEntries.length > 1 && new Set(usableUrlEntries.map(normalizedAuthority)).size > 1) {
    throw new ClientTransportConfigurationError(name, `${usableUrlEntries.map((entry) => entry.key).join(" and ")} disagree; client authority aliases must select the same versioned API authority or only one may be set.`, usableUrlEntries.map((entry) => entry.key));
  }
  const envUrlHit = usableUrlEntries[0] ?? null;
  const keychainUrlHit = keychainConfigValue(name, env, options.credentials?.keychain);
  const profile = options.bindUnpairedCredentialsToFleet ? options.credentials?.profile?.trim() || env.HASNA_PROFILE?.trim() || null : null;
  const diskConfigUrlHit = appConfigDiskValue(name, env, keys.apiUrlKeys, profile);
  if (diskConfigUrlHit?.unusable) {
    throw new ClientTransportConfigurationError(name, `${diskConfigUrlHit.key} in ${diskConfigUrlHit.path} is declared but blank or malformed; public clients require a valid HTTPS service authority.`, [diskConfigUrlHit.path]);
  }
  const urlCandidates = [
    ...envUrlHit ? [envUrlHit] : [],
    ...keychainUrlHit ? [{ key: keychainUrlHit.source, value: keychainUrlHit.value }] : [],
    ...diskConfigUrlHit ? [{ key: diskConfigUrlHit.path, value: diskConfigUrlHit.value.trim() }] : []
  ];
  const configuredUrl = urlCandidates[0] ?? null;
  const normalizedUrls = urlCandidates.map(normalizedAuthority);
  const divergentUrls = urlCandidates.filter((_, index) => normalizedUrls[index] !== normalizedUrls[0]);
  if (configuredUrl && divergentUrls.length > 0) {
    throw new ClientTransportConfigurationError(name, `${configuredUrl.key} and ${divergentUrls.map((candidate) => candidate.key).join(" and ")} select different service authorities; refusing to send a credential written for one authority to the other.`, urlCandidates.map((candidate) => candidate.key));
  }
  if (configuredUrl && options.bindUnpairedCredentialsToFleet) {
    try {
      toV1BaseUrl(configuredUrl.value);
    } catch (error) {
      throw new ClientTransportConfigurationError(name, `Invalid API URL from ${configuredUrl.key}: ${error instanceof Error ? error.message : String(error)}`, [configuredUrl.key]);
    }
  }
  return { keys, configuredUrl, envUrlHit, keychainUrlHit, diskConfigUrlHit };
}
function resolveClientTransportSnapshot(name, env = process.env, options = {}) {
  env = snapshotClientEnvironment(name, env);
  const { keys, configuredUrl, envUrlHit, keychainUrlHit, diskConfigUrlHit } = resolveClientAuthoritySnapshot(name, env, options);
  const warnings = [];
  if (configuredUrl && !envUrlHit) {
    warnings.push(`No ${keys.apiUrlKeys[0]} in the environment; the server URL in ${configuredUrl.key} was used, so this client connects to the server. ` + `Keep that entry aligned with the intended service authority.`);
  }
  const credential = resolveCredential(name, env, options.credentials);
  if (!credential) {
    const diskHint = credentialDiskSourcesForMessage(name, env);
    const lead = configuredUrl ? `${configuredUrl.key} selects the HTTP server for '${name}', but no API key could be resolved` : `${keys.apiUrlKeys[0]} is not set and no API key could be resolved for '${name}'; a credential is required before the default fleet gateway authority applies`;
    warnings.push(`${lead}; refusing to create an unauthenticated client \u2014 public clients never fall back to SQLite or another local store. ` + `Looked in the Keychain (macOS only), then for a credential file at ${diskHint}, then for ${keys.apiKeyKeys[0]} in the environment.`);
    const sources = [configuredUrl?.key ?? keys.apiUrlKeys[0]];
    if (options.bindUnpairedCredentialsToFleet)
      throw new ClientCredentialMissingError(name, warnings.join(" "), sources, configuredUrl !== null);
    throw new ClientTransportConfigurationError(name, warnings.join(" "), sources);
  }
  if (credential.warning)
    warnings.push(credential.warning);
  let urlHit;
  if (configuredUrl) {
    urlHit = configuredUrl;
  } else {
    try {
      urlHit = { key: DEFAULT_AUTHORITY_SOURCE, value: defaultFleetGatewayBaseUrl(name) };
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      throw new ClientTransportConfigurationError(name, `No ${keys.apiUrlKeys[0]} is configured and the default fleet gateway authority cannot be composed for '${name}': ${message}`, [keys.apiUrlKeys[0]]);
    }
  }
  const apiUrlSource = urlHit.key;
  let baseUrl;
  try {
    baseUrl = toV1BaseUrl(urlHit.value);
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    throw new ClientTransportConfigurationError(name, `Invalid API URL from ${apiUrlSource}: ${message}`, [apiUrlSource]);
  }
  if (options.bindUnpairedCredentialsToFleet && baseUrl !== toV1BaseUrl(defaultFleetGatewayBaseUrl(name))) {
    const pairedDisk = diskConfigUrlHit?.path === credential.source && (credential.tier === "disk" || credential.tier === "profile" || credential.tier === "pointer");
    const pairedKeychain = credential.tier === "keychain" && keychainUrlHit?.source === credential.source.replace(".api-key@", ".api-url@");
    const deliberatePair = envUrlHit !== null && (credential.tier === "argument" || credential.tier === "override");
    if (!pairedDisk && !pairedKeychain && !deliberatePair) {
      throw new ClientTransportConfigurationError(name, "An unpaired legacy credential is bound to the fleet gateway. Configure a stored authority/credential pair or an explicit API key override for the intended authority; no credential was sent.");
    }
  }
  return {
    resolution: {
      transport: "http",
      transportSource: urlHit.key,
      baseUrl,
      apiUrlSource,
      apiKeyPresent: true,
      apiKeySource: credential.source,
      apiKeyTier: credential.tier,
      misconfigured: false,
      warning: warnings.length > 0 ? warnings.join(" ") : null
    },
    credential
  };
}
function resolveClientTransport(name, env = process.env, options = {}) {
  return resolveClientTransportSnapshot(name, env, options).resolution;
}
function credentialDiskSourcesForMessage(name, env) {
  const paths = credentialDiskSources(name, env);
  return paths.length > 0 ? paths.join(" or ") : "<no HOME or HASNA_HOME set in this environment, so no credential file was consulted>";
}
var IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
var AUTHORITY_OVERRIDE_HEADERS = new Set([
  "host",
  ":authority",
  "forwarded",
  "x-forwarded-host",
  "x-original-host"
]);

// src/lib/client-storage-policy.ts
var LOCAL_PATH_VARS = [
  "HASNA_DOMAINS_DB_PATH",
  "DOMAINS_DB_PATH",
  "HASNA_DOMAINS_DIR",
  "DOMAINS_DIR"
];
function explicitLocalPathVar(env = process.env) {
  return LOCAL_PATH_VARS.find((key) => (env[key] ?? "").trim() !== "");
}
function domainsAuthorityEnvKeys() {
  const keys = clientTransportEnvKeys("domains");
  return [
    ...keys.apiUrlKeys,
    ...keys.apiKeyKeys,
    credentialOverrideEnvKey("domains"),
    credentialPointerEnvKey("domains"),
    CREDENTIAL_PROFILE_ENV_KEY
  ];
}
function assertDomainsClientStorage(env = process.env) {
  const pathVar = explicitLocalPathVar(env);
  if (pathVar)
    throw new Error(`domains: ${pathVar} is no longer supported by clients. Unset local database path variables and configure the shared API with HASNA_DOMAINS_API_KEY or saved credentials. Preserve any existing database until its records have been migrated and verified.`);
}

// src/sdk/client.ts
class ApiError extends Error {
  status;
  body;
  constructor(status, message, body) {
    super(message);
    this.status = status;
    this.body = body;
    this.name = "ApiError";
  }
}

class DomainsClient {
  baseUrl;
  apiKey;
  fetchImpl;
  baseHeaders;
  constructor(options) {
    if (!options.baseUrl)
      throw new Error("DomainsClient requires a baseUrl.");
    this.baseUrl = options.baseUrl.replace(/\/$/, "");
    this.apiKey = options.apiKey;
    this.fetchImpl = options.fetch ?? globalThis.fetch;
    this.baseHeaders = options.headers ?? {};
  }
  async request(method, path, opts) {
    const url = new URL(this.baseUrl + path);
    if (opts.query) {
      for (const [key, value] of Object.entries(opts.query)) {
        if (value === undefined || value === null)
          continue;
        if (Array.isArray(value)) {
          for (const item of value) {
            if (item !== undefined && item !== null)
              url.searchParams.append(key, String(item));
          }
        } else {
          url.searchParams.set(key, String(value));
        }
      }
    }
    const headers = { Accept: "application/json", ...this.baseHeaders, ...opts.init?.headers };
    if (this.apiKey)
      headers["x-api-key"] = this.apiKey;
    let payload;
    if (opts.body !== undefined) {
      headers["Content-Type"] = "application/json";
      payload = JSON.stringify(opts.body);
    }
    const response = await this.fetchImpl(url.toString(), { ...opts.init, method, headers, body: payload });
    const text = await response.text();
    const data = text ? (() => {
      try {
        return JSON.parse(text);
      } catch {
        return text;
      }
    })() : undefined;
    if (!response.ok) {
      throw new ApiError(response.status, `${method} ${path} failed: ${response.status}`, data);
    }
    return data;
  }
  async getHealth(init) {
    return this.request("GET", `/health`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async getReady(init) {
    return this.request("GET", `/ready`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async checkDomainAvailability(body, init) {
    return this.request("POST", `/v1/availability`, {
      body,
      query: undefined,
      init
    });
  }
  async getDnsRecord(id, init) {
    return this.request("GET", `/v1/dns/${encodeURIComponent(String(id))}`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async deleteDnsRecord(id, init) {
    return this.request("DELETE", `/v1/dns/${encodeURIComponent(String(id))}`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async listDomains(query, init) {
    return this.request("GET", `/v1/domains`, {
      body: undefined,
      query,
      init
    });
  }
  async createDomain(body, init) {
    return this.request("POST", `/v1/domains`, {
      body,
      query: undefined,
      init
    });
  }
  async getDomain(id, init) {
    return this.request("GET", `/v1/domains/${encodeURIComponent(String(id))}`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async deleteDomain(id, init) {
    return this.request("DELETE", `/v1/domains/${encodeURIComponent(String(id))}`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async updateDomain(id, body, init) {
    return this.request("PATCH", `/v1/domains/${encodeURIComponent(String(id))}`, {
      body,
      query: undefined,
      init
    });
  }
  async listDnsRecords(id, init) {
    return this.request("GET", `/v1/domains/${encodeURIComponent(String(id))}/dns`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async createDnsRecord(id, body, init) {
    return this.request("POST", `/v1/domains/${encodeURIComponent(String(id))}/dns`, {
      body,
      query: undefined,
      init
    });
  }
  async listOffers(id, init) {
    return this.request("GET", `/v1/domains/${encodeURIComponent(String(id))}/offers`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async createOffer(id, body, init) {
    return this.request("POST", `/v1/domains/${encodeURIComponent(String(id))}/offers`, {
      body,
      query: undefined,
      init
    });
  }
  async requestDomainProvisioning(body, init) {
    return this.request("POST", `/v1/provisioning`, {
      body,
      query: undefined,
      init
    });
  }
  async adoptOwnedDomainProvisioning(body, init) {
    return this.request("POST", `/v1/provisioning/adopt`, {
      body,
      query: undefined,
      init
    });
  }
  async getDomainProvisioningByName(name, init) {
    return this.request("GET", `/v1/provisioning/by-name/${encodeURIComponent(String(name))}`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async reconcileProvisionedDomainDns(name, body, init) {
    return this.request("POST", `/v1/provisioning/by-name/${encodeURIComponent(String(name))}/dns-reconcile`, {
      body,
      query: undefined,
      init
    });
  }
  async reconcileProvisionedDomainEmailDns(name, body, init) {
    return this.request("POST", `/v1/provisioning/by-name/${encodeURIComponent(String(name))}/dns-reconcile/mail`, {
      body,
      query: undefined,
      init
    });
  }
  async listProvisionedDomainDnsReconciliations(name, query, init) {
    return this.request("GET", `/v1/provisioning/by-name/${encodeURIComponent(String(name))}/dns-reconciliations`, {
      body: undefined,
      query,
      init
    });
  }
  async getProvisionedDomainDnsState(name, query, init) {
    return this.request("GET", `/v1/provisioning/by-name/${encodeURIComponent(String(name))}/dns-state`, {
      body: undefined,
      query,
      init
    });
  }
  async getDomainProvisioning(id, init) {
    return this.request("GET", `/v1/provisioning/${encodeURIComponent(String(id))}`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async advanceDomainProvisioning(id, init) {
    return this.request("POST", `/v1/provisioning/${encodeURIComponent(String(id))}/advance`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async getDomainStats(init) {
    return this.request("GET", `/v1/stats`, {
      body: undefined,
      query: undefined,
      init
    });
  }
  async getVersion(init) {
    return this.request("GET", `/version`, {
      body: undefined,
      query: undefined,
      init
    });
  }
}
// ../contracts/dist/client/storage.js
import { createRequire as createRequire2 } from "module";
var MAX_PACKAGE_BYTES2 = 1024 * 1024;
var MAX_CREDENTIAL_FILE_BYTES2 = 64 * 1024;
var INSPECT_CUSTOM2 = Symbol.for("nodejs.util.inspect.custom");
var CREDENTIAL_SEAL2 = Symbol.for("hasna:contracts:sealedCredential");
var AMBIENT_ENVIRONMENT2 = Symbol.for("hasna:contracts:ambientClientEnvironment");
var requireSecretsSdk2 = createRequire2(import.meta.url);
var IDEMPOTENT_METHODS2 = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
var AUTHORITY_OVERRIDE_HEADERS2 = new Set([
  "host",
  ":authority",
  "forwarded",
  "x-forwarded-host",
  "x-original-host"
]);

// src/lib/domains-resolver.ts
var DOMAINS_APP_NAME = "domains";
var CONTRACTS_AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
function isAmbientEnv(env) {
  if (typeof process !== "undefined" && env === process.env)
    return true;
  return env[CONTRACTS_AMBIENT_ENVIRONMENT] === true;
}
function domainsResolverEnv(env) {
  const blanks = domainsAuthorityEnvKeys().filter((key) => (key in env) && (env[key] ?? "").trim() === "");
  if (blanks.length === 0)
    return env;
  const next = { ...env };
  for (const key of blanks)
    delete next[key];
  return next;
}
function domainsResolverInputs(env, credentials = {}) {
  assertDomainsClientStorage(env);
  const normalised = domainsResolverEnv(env);
  if (normalised === env)
    return { env: normalised, credentials };
  const keychain = { ...credentials.keychain };
  if (keychain.enabled === undefined && keychain.run === undefined) {
    keychain.enabled = isAmbientEnv(env);
  }
  return { env: normalised, credentials: { ...credentials, keychain } };
}

// src/sdk/index.ts
function createDomainsClientFromEnv(env = process.env, overrides = {}) {
  assertDomainsClientStorage(env);
  const { baseUrl: baseUrlOverride, apiKey: apiKeyOverride, profile, keychain, ...rest } = overrides;
  if (baseUrlOverride !== undefined) {
    return new DomainsClient({
      baseUrl: baseUrlOverride,
      ...apiKeyOverride !== undefined ? { apiKey: apiKeyOverride } : {},
      ...rest
    });
  }
  const credentials = {
    ...apiKeyOverride !== undefined ? { apiKey: apiKeyOverride } : {},
    ...profile ? { profile } : {},
    ...keychain ? { keychain } : {}
  };
  const { env: resolverEnv, credentials: resolverCredentials } = domainsResolverInputs(env, credentials);
  const resolution = resolveClientTransport(DOMAINS_APP_NAME, resolverEnv, {
    credentials: resolverCredentials
  });
  const credential = resolveCredential(DOMAINS_APP_NAME, resolverEnv, resolverCredentials);
  if (!credential) {
    throw new Error("domains SDK: no API key resolved from any credential tier; refusing to build an unauthenticated client. " + "Looked at HASNA_DOMAINS_API_KEY_OVERRIDE / HASNA_PROFILE / HASNA_DOMAINS_API_KEY_REF, the Keychain item " + "hasna.credentials.domains.api-key, ~/.hasna/domains/config/credentials, then HASNA_DOMAINS_API_KEY.");
  }
  const baseUrl = resolution.baseUrl.replace(/\/v1$/, "");
  const baseFetch = rest.fetch ?? ((input, init) => fetch(input, init));
  const fetchWithFreshCredential = async (input, init) => {
    const headers = {};
    new Headers(init?.headers ?? {}).forEach((value, key) => {
      headers[key] = value;
    });
    try {
      const fresh = resolveCredential(DOMAINS_APP_NAME, resolverEnv, resolverCredentials);
      if (fresh) {
        const key = fresh.tier === "pointer" ? (await completePointerCredential(DOMAINS_APP_NAME, fresh, resolverEnv)).apiKey : fresh.apiKey;
        headers["x-api-key"] = key;
      }
    } catch {}
    return baseFetch(input, { ...init, headers });
  };
  return new DomainsClient({
    baseUrl,
    apiKey: credential.apiKey,
    ...rest,
    fetch: fetchWithFreshCredential
  });
}
export {
  ApiError,
  DomainsClient,
  createDomainsClientFromEnv
};