import { gracefulExit } from './chunk-XQSWW35O.js';
import { define } from './chunk-WIL4LMMA.js';
import { checkForSchemaErrors, checkForNoEnvFiles, checkForConfigErrors } from './chunk-4MCNC4YY.js';
import { loadVarlockEnvGraph } from './chunk-5SI4EGRL.js';
import { CliExitError } from './chunk-7M6WZ65V.js';
import { __name } from './chunk-IBAHCH6O.js';
import { spawn } from 'child_process';
import 'stream';
import { extname, isAbsolute, delimiter, join } from 'path';
import { accessSync, constants, statSync, existsSync, readFileSync } from 'fs';

function getWindowsExtensions() {
  const pathExt = process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM";
  const exts = pathExt.split(";").filter(Boolean);
  const result = [];
  for (const ext of exts) {
    result.push(ext.toUpperCase());
    result.push(ext.toLowerCase());
  }
  return result;
}
__name(getWindowsExtensions, "getWindowsExtensions");
function isExecutable(filePath) {
  try {
    accessSync(filePath, constants.X_OK);
    const stats = statSync(filePath);
    return stats.isFile();
  } catch {
    return false;
  }
}
__name(isExecutable, "isExecutable");
function isExecutableOnWindows(filePath) {
  if (!existsSync(filePath)) return false;
  try {
    const stats = statSync(filePath);
    if (!stats.isFile()) return false;
  } catch {
    return false;
  }
  const ext = extname(filePath).toLowerCase();
  const extensions = getWindowsExtensions().map((e) => e.toLowerCase());
  return extensions.includes(ext);
}
__name(isExecutableOnWindows, "isExecutableOnWindows");
function readShebang(filePath) {
  try {
    const fd = readFileSync(filePath, { encoding: "utf8", flag: "r" });
    const first150 = fd.slice(0, 150);
    const match = first150.match(/^#!([^\r\n]+)/);
    return match ? match[1].trim() : null;
  } catch {
    return null;
  }
}
__name(readShebang, "readShebang");
function findCommand(command) {
  const isWin = process.platform === "win32";
  const hasPathSep = command.includes("/") || command.includes("\\");
  if (isAbsolute(command) || hasPathSep) {
    return command;
  }
  const pathEnv = process.env.PATH || "";
  const searchPaths = [];
  if (isWin) {
    searchPaths.push(process.cwd());
  }
  const pathParts = pathEnv.split(delimiter);
  for (const part of pathParts) {
    const cleanPart = /^".*"$/.test(part) ? part.slice(1, -1) : part;
    if (cleanPart) {
      searchPaths.push(cleanPart);
    }
  }
  let extensions = [""];
  if (isWin) {
    extensions = getWindowsExtensions();
    if (command.includes(".") && process.env.PATHEXT) {
      extensions.unshift("");
    }
  }
  for (const dir of searchPaths) {
    for (const ext of extensions) {
      const fullPath = join(dir, command + ext);
      if (isWin) {
        if (isExecutableOnWindows(fullPath)) {
          return fullPath;
        }
      } else {
        if (isExecutable(fullPath)) {
          const shebang = readShebang(fullPath);
          if (shebang && shebang.startsWith("/")) {
            return fullPath;
          } else if (!shebang) {
            return fullPath;
          }
        }
      }
    }
  }
  return command;
}
__name(findCommand, "findCommand");
function escapeCmdExeArg(str) {
  return str.replace(/"/g, '""');
}
__name(escapeCmdExeArg, "escapeCmdExeArg");
function exec(command, args, options = {}) {
  const resolvedCommand = findCommand(command);
  const isCommandFound = resolvedCommand !== command || isAbsolute(command) || command.includes("/") || command.includes("\\");
  const needsShell = process.platform === "win32" && (/\.(cmd|bat)$/i.test(resolvedCommand) || !isCommandFound);
  let spawnCommand = resolvedCommand;
  let spawnArgs = args;
  const spawnOptions = {
    env: options.env || process.env,
    shell: false
  };
  if (needsShell) {
    const cmdToUse = isCommandFound ? resolvedCommand : command;
    const quotedCmd = `"${escapeCmdExeArg(cmdToUse)}"`;
    const cmdStr = [quotedCmd, ...args.map((a) => `"${escapeCmdExeArg(a)}"`)].join(" ");
    spawnArgs = ["/d", "/s", "/c", `"${cmdStr}"`];
    spawnCommand = process.env.comspec || "cmd.exe";
    spawnOptions.windowsVerbatimArguments = true;
  }
  let stdio;
  if (options.stdio === "inherit") {
    stdio = "inherit";
  } else if (options.stdio === "pipe") {
    stdio = ["pipe", "pipe", "pipe"];
  } else if (options.stdio) {
    stdio = options.stdio;
  } else {
    stdio = [
      options.stdin || "inherit",
      options.stdout || "inherit",
      options.stderr || "inherit"
    ];
  }
  spawnOptions.stdio = stdio;
  const childProcess = spawn(spawnCommand, spawnArgs, spawnOptions);
  const result = {
    stdout: childProcess.stdout || void 0,
    stderr: childProcess.stderr || void 0,
    pid: childProcess.pid,
    kill: /* @__PURE__ */ __name((signal) => childProcess.kill(signal), "kill")
  };
  const promise = new Promise((resolve, reject) => {
    let errorEmitted = false;
    childProcess.on("error", (error) => {
      errorEmitted = true;
      reject(
        Object.assign(error, {
          exitCode: 1,
          ...result
        })
      );
    });
    childProcess.on("exit", (code, signal) => {
      if (process.platform === "win32" && code === 1 && !errorEmitted && !existsSync(resolvedCommand)) {
        const error = new Error(`Command not found: ${command}`);
        error.code = "ENOENT";
        error.exitCode = 1;
        Object.assign(error, result);
        reject(error);
        return;
      }
      const exitCode = code ?? (signal ? 1 : 0);
      const exitResult = {
        exitCode,
        signal: signal || void 0,
        ...result
      };
      if (exitCode !== 0) {
        const error = new Error(`Command failed with exit code ${exitCode}`);
        error.exitCode = exitCode;
        error.signal = signal;
        Object.assign(error, result);
        reject(error);
      } else {
        resolve(exitResult);
      }
    });
  });
  Object.assign(promise, result);
  return promise;
}
__name(exec, "exec");

// src/cli/commands/run.command.ts
var commandSpec = define({
  name: "run",
  description: "Run a command with your environment variables injected",
  args: {
    // watch: {
    //   type: 'boolean',
    //   short: 'w',
    //   description: 'Watch mode',
    // },
    "redact-stdout": {
      type: "boolean",
      negatable: true,
      description: "Override automatic stdout/stderr redaction: --redact-stdout forces redaction of piped/redirected output (e.g., to override @redactLogs=false) and errors if attached to an interactive terminal; --no-redact-stdout disables redaction entirely. Can also be set via the _VARLOCK_REDACT_STDOUT env var (the flag takes precedence)"
    },
    inject: {
      type: "string",
      short: "i",
      description: 'Control what gets injected into the child process env: "all" (default), "vars" (individual vars only, no blob), or "blob" (only __VARLOCK_ENV blob, no individual vars)'
    },
    "no-inject-graph": {
      type: "boolean",
      description: "Deprecated: use --inject vars instead"
    },
    path: {
      type: "string",
      short: "p",
      multiple: true,
      description: "Path to a specific .env file or directory to use as the entry point (can be specified multiple times)"
    },
    "clear-cache": {
      type: "boolean",
      description: "Clear cache and re-resolve all values"
    },
    "skip-cache": {
      type: "boolean",
      description: "Skip cache entirely for this invocation"
    }
  },
  examples: `
Executes a command in a child process, injecting your resolved and validated environment
variables from your .env files. Useful when a code-level integration is not possible.

Examples:
  varlock run -- node app.js                    # Run a Node.js application
  varlock run -- python script.py               # Run a Python script
  varlock run -- sh -c 'echo $MY_VAR'           # Use shell expansion for env vars
  varlock run --inject vars -- sh               # Inject only individual vars, no blob
  varlock run --inject blob -- node app.js      # Inject only the blob, no individual vars
  varlock run --path .env.prod -- node app.js   # Use a specific .env file
  varlock run --path ./config/ -- node app.js   # Use a specific directory
  varlock run -p ./envs -p ./overrides -- node app.js  # Use multiple directories

\u{1F4CD} Important: Use -- to separate varlock options from your command

\u{1F4A1} Tip: For shell expansion of env vars, use: sh -c 'your command here'
\u{1F4A1} Tip: Output redaction applies automatically when output is piped/redirected (e.g., CI logs);
   interactive terminals get raw TTY pass-through, so tools like psql and claude just work.
   Use --no-redact-stdout to disable redaction for piped output, or --redact-stdout to
   force it (e.g., to override @redactLogs=false).
\u{1F4A1} Tip: Use --inject vars to prevent __VARLOCK_ENV from being visible in child process env
\u{1F4A1} Tip: Use --inject blob when your app uses the ENV proxy and doesn't need individual process.env vars
  `.trim()
});
function parseEnvToggle(value) {
  if (value === void 0) return void 0;
  const normalized = value.trim().toLowerCase();
  if (normalized === "1" || normalized === "true") return true;
  if (normalized === "0" || normalized === "false") return false;
  return void 0;
}
__name(parseEnvToggle, "parseEnvToggle");
var commandProcess;
var childCommandKilledFromRestart = false;
var commandFn = /* @__PURE__ */ __name(async (ctx) => {
  const argv = process.argv.slice(2);
  let restCommandArgs = [];
  if (argv.includes("--")) {
    const doubleDashIndex = argv.indexOf("--");
    restCommandArgs = argv.slice(doubleDashIndex + 1);
  } else {
    throw new Error("No command to run! Your command should look like `varlock run -- <your-command>`");
  }
  const commandToRunAsArgs = restCommandArgs;
  const commandToRunStr = restCommandArgs.join(" ");
  const rawCommand = commandToRunAsArgs[0];
  const commandArgsOnly = commandToRunAsArgs.slice(1);
  const envGraph = await loadVarlockEnvGraph({
    entryFilePaths: ctx.values.path,
    clearCache: ctx.values["clear-cache"],
    skipCache: ctx.values["skip-cache"]
  });
  checkForSchemaErrors(envGraph);
  checkForNoEnvFiles(envGraph);
  await envGraph.generateTypesIfNeeded();
  await envGraph.resolveEnvValues();
  checkForConfigErrors(envGraph);
  const resolvedEnv = envGraph.getResolvedEnvObject();
  const serializedGraph = envGraph.getSerializedGraph();
  const { resetRedactionMap } = await import('./runtime/env.js');
  const { createRedactedStreamWriter } = await import('./redact-stream-HPOXW4IG.js');
  let injectDefault = "all";
  if (ctx.values["no-inject-graph"]) {
    console.warn("[varlock] \u26A0\uFE0F  --no-inject-graph is deprecated, use --inject vars instead");
    injectDefault = "vars";
  }
  const injectMode = ctx.values.inject ?? injectDefault;
  const validModes = ["all", "vars", "blob"];
  if (!validModes.includes(injectMode)) {
    throw new CliExitError(`Invalid --inject mode: "${injectMode}". Must be one of: ${validModes.join(", ")}`);
  }
  const injectVars = injectMode === "all" || injectMode === "vars";
  const injectBlob = injectMode === "all" || injectMode === "blob";
  const fullInjectedEnv = {
    ...process.env,
    ...injectVars ? resolvedEnv : {},
    __VARLOCK_RUN: "1",
    // flag for a child process to detect it is running via `varlock run`
    ...injectBlob ? { __VARLOCK_ENV: JSON.stringify(serializedGraph) } : {}
  };
  if (injectBlob && !injectVars && process.env._VARLOCK_ENV_KEY) {
    fullInjectedEnv._VARLOCK_ENV_KEY = process.env._VARLOCK_ENV_KEY;
  }
  const redactLogs = serializedGraph.settings?.redactLogs ?? true;
  const redactOverride = ctx.values["redact-stdout"] ?? parseEnvToggle(process.env._VARLOCK_REDACT_STDOUT);
  const forceRedact = redactOverride === true;
  const forceNoRedact = redactOverride === false;
  if (forceRedact && (process.stdout.isTTY || process.stderr.isTTY)) {
    throw new CliExitError("Cannot force redaction while output is attached to an interactive terminal", {
      details: [
        "Redaction requires piping stdout/stderr, which breaks tools that need a raw TTY (e.g., claude, psql).",
        "Redaction is applied automatically whenever output is piped or redirected, so you can likely just drop the --redact-stdout flag."
      ]
    });
  }
  const redactionEnabled = !forceNoRedact && (redactLogs || forceRedact);
  const redactStdout = redactionEnabled && (forceRedact || !process.stdout.isTTY);
  const redactStderr = redactionEnabled && (forceRedact || !process.stderr.isTTY);
  if (!redactStdout && !redactStderr) {
    commandProcess = exec(rawCommand, commandArgsOnly, {
      stdio: "inherit",
      env: fullInjectedEnv
    });
  } else {
    resetRedactionMap(serializedGraph);
    commandProcess = exec(rawCommand, commandArgsOnly, {
      stdin: "inherit",
      stdout: redactStdout ? "pipe" : "inherit",
      stderr: redactStderr ? "pipe" : "inherit",
      env: fullInjectedEnv
    });
    if (redactStdout && commandProcess.stdout) {
      const stdoutWriter = createRedactedStreamWriter(process.stdout);
      commandProcess.stdout.on("data", stdoutWriter.write);
      commandProcess.stdout.on("close", stdoutWriter.flush);
    }
    if (redactStderr && commandProcess.stderr) {
      const stderrWriter = createRedactedStreamWriter(process.stderr);
      commandProcess.stderr.on("data", stderrWriter.write);
      commandProcess.stderr.on("close", stderrWriter.flush);
    }
  }
  {
    process.on("exit", (_code, _signal) => {
      commandProcess?.kill(9);
    });
    ["SIGTERM", "SIGINT"].forEach((signal) => {
      process.on(signal, () => {
        commandProcess?.kill(9);
        gracefulExit(1);
      });
    });
  }
  let exitCode;
  try {
    const result = await commandProcess;
    exitCode = result.exitCode;
  } catch (error) {
    if (error.signal === "SIGINT" && childCommandKilledFromRestart) ;
    if (error.signal === "SIGINT" || error.signal === "SIGKILL") {
      gracefulExit(1);
    } else {
      console.log(error.message);
      console.log(`command [${commandToRunStr}] failed`);
      console.log("try running the same command without varlock");
      console.log("if you get a different result, varlock may be the problem...");
    }
    exitCode = error.exitCode || 1;
  }
  {
    return gracefulExit(exitCode);
  }
}, "commandFn");

export { commandFn, commandSpec };
//# sourceMappingURL=chunk-UYYR74JF.js.map
//# sourceMappingURL=chunk-UYYR74JF.js.map