#!/usr/bin/env node

// src/index.ts
import { Command } from "commander";
import { spawn as spawn3 } from "child_process";

// src/commands/login.ts
import chalk2 from "chalk";
import readline from "readline";
import { spawn } from "child_process";

// src/config.ts
import dotenv from "dotenv";
import fs from "fs";
import path from "path";
import chalk from "chalk";
dotenv.config();
var CONFIG_SCHEMA = {
  api_key: {
    envVar: "HUMANLAYER_API_KEY",
    configKey: "api_key",
    flagKey: "apiKey",
    required: true
  },
  api_base_url: {
    envVar: "HUMANLAYER_API_BASE",
    configKey: "api_base_url",
    flagKey: "apiBase",
    defaultValue: "https://api.humanlayer.dev/humanlayer/v1",
    required: true
  },
  app_base_url: {
    envVar: "HUMANLAYER_APP_URL",
    configKey: "app_base_url",
    flagKey: "appBase",
    defaultValue: "https://app.humanlayer.dev",
    required: true
  },
  daemon_socket: {
    envVar: "HUMANLAYER_DAEMON_SOCKET",
    configKey: "daemon_socket",
    flagKey: "daemonSocket",
    defaultValue: "~/.humanlayer/daemon.sock",
    required: true
  },
  run_id: {
    envVar: "HUMANLAYER_RUN_ID",
    configKey: "run_id",
    flagKey: "runId",
    required: false
  }
};
var ConfigResolver = class {
  constructor(options = {}) {
    this.configFile = this.loadConfigFile(options.configFile);
    this.configFilePath = this.getConfigFilePath(options.configFile);
  }
  loadConfigFile(configFile) {
    if (configFile) {
      const configContent = fs.readFileSync(configFile, "utf8");
      return JSON.parse(configContent);
    }
    const configPaths = ["humanlayer.json", getDefaultConfigPath()];
    for (const configPath of configPaths) {
      try {
        if (fs.existsSync(configPath)) {
          const configContent = fs.readFileSync(configPath, "utf8");
          return JSON.parse(configContent);
        }
      } catch (error) {
        console.error(chalk.yellow(`Warning: Could not parse config file ${configPath}: ${error}`));
      }
    }
    return { channel: {} };
  }
  getConfigFilePath(configFile) {
    if (configFile) return configFile;
    const configPaths = ["humanlayer.json", getDefaultConfigPath()];
    for (const configPath of configPaths) {
      try {
        if (fs.existsSync(configPath)) {
          return configPath;
        }
      } catch {
      }
    }
    return getDefaultConfigPath();
  }
  resolveValue(key, options = {}) {
    const schema = CONFIG_SCHEMA[key];
    if (options[schema.flagKey]) {
      return {
        value: options[schema.flagKey],
        source: "flag",
        sourceName: "flag"
      };
    }
    const envValue = process.env[schema.envVar];
    if (envValue) {
      return {
        value: envValue,
        source: "env",
        sourceName: schema.envVar
      };
    }
    const configValue = this.configFile[schema.configKey];
    if (configValue) {
      return {
        value: configValue,
        source: "config",
        sourceName: this.configFilePath
      };
    }
    if (schema.defaultValue) {
      return {
        value: schema.defaultValue,
        source: "default",
        sourceName: "default"
      };
    }
    return {
      value: void 0,
      source: "none",
      sourceName: "none"
    };
  }
  resolveAll(options = {}) {
    const api_key = this.resolveValue("api_key", options);
    const api_base_url = this.resolveValue("api_base_url", options);
    const app_base_url = this.resolveValue("app_base_url", options);
    const daemon_socket = this.resolveValue("daemon_socket", options);
    const run_id = this.resolveValue("run_id", options);
    return {
      api_key,
      api_base_url,
      app_base_url,
      daemon_socket,
      run_id,
      contact_channel: buildContactChannel(options, this.configFile)
    };
  }
  // Legacy compatibility
  resolveFullConfig(options = {}) {
    const resolved = this.resolveAll(options);
    return {
      api_key: resolved.api_key.value,
      api_base_url: resolved.api_base_url.value,
      app_base_url: resolved.app_base_url.value,
      daemon_socket: resolved.daemon_socket.value,
      run_id: resolved.run_id.value,
      contact_channel: resolved.contact_channel
    };
  }
};
function loadConfigFile(configFile) {
  return new ConfigResolver({ configFile }).configFile;
}
function buildContactChannel(options, config) {
  const channel = config.channel || {};
  const slackChannelId = options.slackChannel || process.env.HUMANLAYER_SLACK_CHANNEL || channel.slack?.channel_or_user_id || "";
  const slackBotToken = options.slackBotToken || process.env.HUMANLAYER_SLACK_BOT_TOKEN || channel.slack?.bot_token;
  const slackContext = options.slackContext || process.env.HUMANLAYER_SLACK_CONTEXT || channel.slack?.context_about_channel_or_user;
  const slackThreadTs = options.slackThreadTs || process.env.HUMANLAYER_SLACK_THREAD_TS || channel.slack?.thread_ts;
  const slackBlocks = options.slackBlocks !== void 0 ? options.slackBlocks : process.env.HUMANLAYER_SLACK_BLOCKS === "true" || channel.slack?.experimental_slack_blocks || true;
  const emailAddress = options.emailAddress || process.env.HUMANLAYER_EMAIL_ADDRESS || channel.email?.address;
  const emailContext = options.emailContext || process.env.HUMANLAYER_EMAIL_CONTEXT || channel.email?.context_about_user;
  const contactChannel = {};
  if (slackChannelId || slackBotToken) {
    contactChannel.slack = {
      channel_or_user_id: slackChannelId,
      experimental_slack_blocks: slackBlocks
    };
    if (slackBotToken) contactChannel.slack.bot_token = slackBotToken;
    if (slackContext) contactChannel.slack.context_about_channel_or_user = slackContext;
    if (slackThreadTs) contactChannel.slack.thread_ts = slackThreadTs;
  }
  if (emailAddress) {
    contactChannel.email = {
      address: emailAddress
    };
    if (emailContext) contactChannel.email.context_about_user = emailContext;
  }
  return contactChannel;
}
function saveConfigFile(config, configFile) {
  const configPath = configFile || getDefaultConfigPath();
  console.log(chalk.yellow(`Writing config to ${configPath}`));
  const configDir = path.dirname(configPath);
  fs.mkdirSync(configDir, { recursive: true });
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
  console.log(chalk.green("Config saved successfully"));
}
function getDefaultConfigPath() {
  const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(process.env.HOME || "", ".config");
  return path.join(xdgConfigHome, "humanlayer", "humanlayer.json");
}
function resolveConfigWithSources(options = {}) {
  const resolver = new ConfigResolver(options);
  const resolved = resolver.resolveAll(options);
  return {
    api_key: resolved.api_key,
    api_base_url: resolved.api_base_url,
    app_base_url: resolved.app_base_url,
    daemon_socket: resolved.daemon_socket,
    run_id: resolved.run_id,
    contact_channel: resolved.contact_channel
  };
}
function resolveFullConfig(options = {}) {
  const resolver = new ConfigResolver(options);
  return resolver.resolveFullConfig(options);
}
function maskSensitiveValue(value) {
  if (!value) {
    return "<not set>";
  }
  if (value.length <= 6) {
    return value + "...";
  }
  return value.substring(0, 6) + "...";
}

// src/hlClient.ts
function getProject(apiBase, apiKey) {
  return fetch(`${apiBase}/project`, {
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    }
  }).then((res) => {
    if (!res.ok) {
      throw new Error(`Failed to get project: ${res.status} ${res.statusText}`);
    }
    return res.json();
  });
}

// src/commands/login.ts
async function loginCommand(options) {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  const question = (prompt) => {
    return new Promise((resolve) => {
      rl.question(prompt, resolve);
    });
  };
  try {
    const existingConfig = options.configFile ? loadConfigFile(options.configFile) : { channel: {} };
    const config = resolveFullConfig({ ...options, configFile: options.configFile });
    const appUrl = options.appBase || config.app_base_url;
    const loginUrl = `${appUrl}/cli-login`;
    console.log(chalk2.blue("HumanLayer Login"));
    const openBrowser = async (url) => {
      const platform = process.platform;
      let command;
      if (platform === "darwin") {
        command = "open";
      } else if (platform === "win32") {
        command = "start";
      } else {
        command = "xdg-open";
      }
      try {
        spawn(command, [url], { detached: true, stdio: "ignore" }).unref();
        console.log(chalk2.green("Opening browser..."));
      } catch (error) {
        console.log(chalk2.yellow(`Could not open browser automatically: ${error}`));
        console.log(chalk2.gray(`To get your API token, visit: ${loginUrl}`));
      }
    };
    const browserPrompt = await question(
      "Press Enter to open in default browser or ESC to continue manually: "
    );
    if (browserPrompt !== "\x1B") {
      await openBrowser(loginUrl);
    }
    console.log("");
    const token = await question("Paste your API token: ");
    if (!token.trim()) {
      console.error(chalk2.red("Error: No token provided"));
      process.exit(1);
    }
    const configPath = options.configFile || getDefaultConfigPath();
    console.log(chalk2.yellow(`Token will be written to: ${configPath}`));
    const proceed = await question("Continue? (Y/n): ");
    if (proceed.toLowerCase() === "n" || proceed.toLowerCase() === "no") {
      console.log(chalk2.gray("Login cancelled"));
      process.exit(0);
    }
    const apiBaseUrl = options.apiBase || config.api_base_url;
    let project;
    try {
      project = await getProject(apiBaseUrl, token.trim());
    } catch (error) {
      console.error(chalk2.red(`Returned token was invalid: ${error}`));
      process.exit(1);
    }
    const newConfig = {
      ...existingConfig,
      api_key: token.trim()
    };
    if (options.apiBase) {
      newConfig.api_base_url = options.apiBase;
    }
    if (options.appBase) {
      newConfig.app_base_url = options.appBase;
    }
    saveConfigFile(newConfig, options.configFile);
    console.log(chalk2.green(`Login successful, using project ${chalk2.bold(project.name)}`));
  } catch (error) {
    console.error(chalk2.red(`Error during login: ${error}`));
    process.exit(1);
  } finally {
    rl.close();
  }
}

// src/commands/tui.ts
import { spawn as spawn2 } from "child_process";
import { join } from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";
import { existsSync } from "fs";
import { homedir } from "os";
import { connect } from "net";
async function isDaemonRunning(socketPath) {
  return new Promise((resolve) => {
    const client = connect(socketPath, () => {
      client.end();
      resolve(true);
    });
    client.on("error", () => {
      resolve(false);
    });
    client.setTimeout(1e3, () => {
      client.destroy();
      resolve(false);
    });
  });
}
async function startDaemon(daemonPath, config) {
  return new Promise((resolve, reject) => {
    console.log("Starting HumanLayer daemon...");
    const env = { ...process.env };
    if (config.api_key) {
      env.HUMANLAYER_API_KEY = config.api_key;
    }
    if (config.api_base_url) {
      env.HUMANLAYER_API_BASE_URL = config.api_base_url;
    }
    const daemon = spawn2(daemonPath, [], {
      detached: true,
      stdio: "ignore",
      env
    });
    daemon.on("error", (err) => {
      reject(new Error(`Failed to start daemon: ${err.message}`));
    });
    daemon.on("spawn", () => {
      daemon.unref();
      setTimeout(() => {
        console.log("Daemon started successfully");
        resolve();
      }, 2e3);
    });
  });
}
var tuiCommand = async (options = {}) => {
  let child = null;
  try {
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);
    const config = resolveFullConfig(options);
    let socketPath = config.daemon_socket;
    if (socketPath.startsWith("~")) {
      socketPath = join(homedir(), socketPath.slice(1));
    }
    const daemonRunning = await isDaemonRunning(socketPath);
    if (!daemonRunning) {
      const daemonPath = join(__dirname, "./bin/hld");
      if (!existsSync(daemonPath)) {
        console.error("Daemon binary not found at:", daemonPath);
        console.error("Please ensure the HumanLayer daemon is installed.");
        process.exit(1);
      }
      try {
        await startDaemon(daemonPath, config);
      } catch (err) {
        console.error("Failed to start daemon:", err);
        console.error("You can try starting it manually with: hld");
        process.exit(1);
      }
    }
    const binaryPath = join(__dirname, "./bin/humanlayer-tui");
    const env = { ...process.env };
    if (socketPath !== join(homedir(), ".humanlayer", "daemon.sock")) {
      env.HUMANLAYER_DAEMON_SOCKET = socketPath;
    }
    child = spawn2(binaryPath, [], { stdio: "inherit", env });
    child.on("exit", (code) => process.exit(code ?? 0));
    child.on("error", (err) => {
      console.error("Failed to start the TUI binary:", err);
      process.exit(1);
    });
    const handleSignal = (signal) => {
      if (child) {
        child.kill(signal);
      }
    };
    process.on("SIGINT", handleSignal);
    process.on("SIGTERM", handleSignal);
    process.on("SIGHUP", handleSignal);
    process.on("exit", () => {
      if (child) {
        child.kill();
      }
    });
  } catch (error) {
    console.error("Error running tui:", error);
    if (child) {
      child.kill();
    }
    process.exit(1);
  }
};

// src/commands/contactHuman.ts
import chalk3 from "chalk";
import { humanlayer } from "@humanlayer/sdk";
async function contactHumanCommand(options) {
  let message = options.message;
  if (message === "-") {
    process.stdin.setEncoding("utf8");
    let stdinData = "";
    for await (const chunk of process.stdin) {
      stdinData += chunk;
    }
    message = stdinData.trim();
  }
  try {
    const resolvedConfig = resolveFullConfig(options);
    const hl = Object.keys(resolvedConfig.contact_channel).length > 0 ? humanlayer({
      contactChannel: resolvedConfig.contact_channel,
      ...resolvedConfig.run_id && { runId: resolvedConfig.run_id }
    }) : humanlayer({
      apiKey: resolvedConfig.api_key,
      apiBaseUrl: resolvedConfig.api_base_url,
      ...resolvedConfig.run_id && { runId: resolvedConfig.run_id }
    });
    console.error(chalk3.yellow("Contacting human..."));
    const response = await hl.fetchHumanResponse({
      spec: {
        msg: message
      }
    });
    console.error(chalk3.green("Human response received"));
    console.log(response);
  } catch (error) {
    console.error(chalk3.red("Error contacting human:"), error);
    process.exit(1);
  }
}

// src/commands/configShow.ts
import chalk4 from "chalk";
function configShowCommand(options) {
  try {
    const resolvedConfig = resolveFullConfig(options);
    if (options.json) {
      const jsonOutput = {
        api_key: resolvedConfig.api_key ? maskSensitiveValue(resolvedConfig.api_key) : void 0,
        api_base_url: resolvedConfig.api_base_url,
        app_base_url: resolvedConfig.app_base_url,
        contact_channel: resolvedConfig.contact_channel
      };
      if (jsonOutput.contact_channel.slack?.bot_token) {
        jsonOutput.contact_channel.slack.bot_token = maskSensitiveValue(
          jsonOutput.contact_channel.slack.bot_token
        );
      }
      console.log(JSON.stringify(jsonOutput, null, 2));
      return;
    }
    console.log(chalk4.blue("HumanLayer Configuration"));
    console.log(chalk4.gray("=".repeat(50)));
    console.log("");
    console.log(chalk4.yellow("Config File Sources:"));
    const configPath = options.configFile || getDefaultConfigPath();
    console.log(`  Primary: ${configPath}`);
    console.log(`  Local: humanlayer.json`);
    console.log("");
    console.log(chalk4.yellow("API Configuration:"));
    const apiKey = resolvedConfig.api_key || "<not set>";
    const displayApiKey = apiKey === "<not set>" ? chalk4.red(apiKey) : chalk4.green(maskSensitiveValue(apiKey));
    console.log(`  API Key: ${displayApiKey}`);
    console.log(`  API Base URL: ${chalk4.cyan(resolvedConfig.api_base_url)}`);
    console.log(`  App Base URL: ${chalk4.cyan(resolvedConfig.app_base_url)}`);
    console.log("");
    console.log(chalk4.yellow("Contact Channel Configuration:"));
    const contactChannel = resolvedConfig.contact_channel;
    if (contactChannel.slack) {
      console.log(`  Slack:`);
      console.log(
        `    Channel/User ID: ${chalk4.cyan(contactChannel.slack.channel_or_user_id || "<not set>")}`
      );
      const displayBotToken = contactChannel.slack.bot_token ? chalk4.green(maskSensitiveValue(contactChannel.slack.bot_token)) : chalk4.red("<not set>");
      console.log(`    Bot Token: ${displayBotToken}`);
      console.log(
        `    Context: ${chalk4.cyan(contactChannel.slack.context_about_channel_or_user || "<not set>")}`
      );
      console.log(`    Thread TS: ${chalk4.cyan(contactChannel.slack.thread_ts || "<not set>")}`);
      console.log(`    Blocks: ${chalk4.cyan(contactChannel.slack.experimental_slack_blocks)}`);
    }
    if (contactChannel.email) {
      console.log(`  Email:`);
      console.log(`    Address: ${chalk4.cyan(contactChannel.email.address)}`);
      console.log(`    Context: ${chalk4.cyan(contactChannel.email.context_about_user || "<not set>")}`);
    }
    if (!contactChannel.slack && !contactChannel.email) {
      console.log(chalk4.gray("  No contact channel configured"));
    }
    console.log("");
    console.log(chalk4.yellow("Configuration Sources:"));
    const configWithSources = resolveConfigWithSources(options);
    if (configWithSources.api_key?.value) {
      const displayValue = maskSensitiveValue(configWithSources.api_key.value);
      console.log(
        `  API Key: ${chalk4.green(displayValue)} ${chalk4.gray(
          `(${configWithSources.api_key.sourceName})`
        )}`
      );
    }
    if (configWithSources.api_base_url.source !== "default") {
      console.log(
        `  API Base URL: ${chalk4.cyan(configWithSources.api_base_url.value)} ${chalk4.gray(
          `(${configWithSources.api_base_url.sourceName})`
        )}`
      );
    }
    if (configWithSources.app_base_url.source !== "default") {
      console.log(
        `  App Base URL: ${chalk4.cyan(configWithSources.app_base_url.value)} ${chalk4.gray(
          `(${configWithSources.app_base_url.sourceName})`
        )}`
      );
    }
    const otherEnvVars = [
      "HUMANLAYER_SLACK_CHANNEL",
      "HUMANLAYER_SLACK_BOT_TOKEN",
      "HUMANLAYER_SLACK_CONTEXT",
      "HUMANLAYER_SLACK_THREAD_TS",
      "HUMANLAYER_SLACK_BLOCKS",
      "HUMANLAYER_EMAIL_ADDRESS",
      "HUMANLAYER_EMAIL_CONTEXT"
    ];
    otherEnvVars.forEach((envVar) => {
      const value = process.env[envVar];
      if (value) {
        const displayValue = envVar.includes("TOKEN") || envVar.includes("KEY") ? maskSensitiveValue(value) : value;
        console.log(`  ${envVar}: ${chalk4.green(displayValue)} ${chalk4.gray("(env)")}`);
      }
    });
    const hasAdditionalConfig = configWithSources.api_key?.value || configWithSources.api_base_url.source !== "default" || configWithSources.app_base_url.source !== "default" || otherEnvVars.some((envVar) => process.env[envVar]);
    if (!hasAdditionalConfig) {
      console.log(chalk4.gray("  Using default configuration"));
    }
  } catch (error) {
    console.error(chalk4.red(`Error showing config: ${error}`));
    process.exit(1);
  }
}

// src/commands/ping.ts
import chalk5 from "chalk";
async function pingCommand() {
  try {
    const config = resolveFullConfig({});
    if (!config.api_key) {
      console.error(chalk5.red("Error: No HumanLayer API token found."));
      console.error(chalk5.gray('Run "humanlayer login" to authenticate.'));
      process.exit(1);
    }
    console.log(chalk5.blue("Checking authentication..."));
    const project = await getProject(config.api_base_url, config.api_key);
    console.log(chalk5.green("\u2713 Authentication successful"));
    console.log(chalk5.blue(`Project: ${chalk5.bold(project.name)}`));
    console.log(chalk5.gray(`Organization: ${project.organization}`));
    console.log(chalk5.gray(`API Base: ${config.api_base_url}`));
  } catch (error) {
    console.error(chalk5.red("\u2717 Authentication failed"));
    console.error(chalk5.red(`Error: ${error}`));
    console.error(chalk5.gray('Run "humanlayer login" to re-authenticate.'));
    process.exit(1);
  }
}

// src/commands/launch.ts
import { connect as connect2 } from "net";
import { homedir as homedir2 } from "os";
import { join as join2 } from "path";
async function launchSession(socketPath, prompt, options = {}) {
  return new Promise((resolve, reject) => {
    const client = connect2(socketPath, () => {
      const mcpConfig = options.approvals !== false ? {
        mcpServers: {
          approvals: {
            command: "npx",
            args: ["humanlayer", "mcp", "claude_approvals"]
          }
        }
      } : void 0;
      const request = {
        jsonrpc: "2.0",
        method: "launchSession",
        params: {
          prompt,
          model: options.model,
          working_dir: options.workingDir || process.cwd(),
          max_turns: options.maxTurns,
          mcp_config: mcpConfig,
          permission_prompt_tool: mcpConfig ? "mcp__approvals__request_permission" : void 0
        },
        id: 1
      };
      client.write(JSON.stringify(request) + "\n");
    });
    let buffer = "";
    client.on("data", (data) => {
      buffer += data.toString();
      const lines = buffer.split("\n");
      buffer = lines.pop() || "";
      for (const line of lines) {
        if (line.trim()) {
          try {
            const response = JSON.parse(line);
            client.end();
            if (response.error) {
              reject(new Error(`RPC Error: ${response.error.message}`));
            } else {
              resolve(response.result);
            }
          } catch (err) {
            reject(new Error(`Failed to parse response: ${err}`));
          }
        }
      }
    });
    client.on("error", (err) => {
      reject(new Error(`Connection failed: ${err.message}`));
    });
  });
}
var launchCommand = async (prompt, options = {}) => {
  try {
    const config = resolveFullConfig(options);
    let socketPath = config.daemon_socket;
    if (socketPath.startsWith("~")) {
      socketPath = join2(homedir2(), socketPath.slice(1));
    }
    console.log("Launching Claude Code session...");
    console.log("Prompt:", prompt);
    if (options.model) console.log("Model:", options.model);
    console.log("Working directory:", options.workingDir || process.cwd());
    console.log("Approvals enabled:", options.approvals !== false);
    const result = await launchSession(socketPath, prompt, options);
    console.log("\nSession launched successfully!");
    console.log("Session ID:", result.session_id);
    console.log("Run ID:", result.run_id);
    console.log('\nYou can now use "npx humanlayer tui" to manage approvals for this session.');
  } catch (error) {
    console.error("Failed to launch session:", error);
    console.error("\nMake sure the daemon is running. You can start it with:");
    console.error("  hld");
    process.exit(1);
  }
};

// src/mcp.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError
} from "@modelcontextprotocol/sdk/types.js";
import { humanlayer as humanlayer2 } from "@humanlayer/sdk";
function validateAuth() {
  const config = resolveFullConfig({});
  if (!config.api_key) {
    console.error("Error: No HumanLayer API token found.");
    console.error("Please set HUMANLAYER_API_KEY environment variable or run `humanlayer login`");
    process.exit(1);
  }
}
async function startDefaultMCPServer() {
  validateAuth();
  const server = new Server(
    {
      name: "humanlayer-standalone",
      version: "1.0.0"
    },
    {
      capabilities: {
        tools: {}
      }
    }
  );
  const resolvedConfig = resolveFullConfig({});
  const hl = humanlayer2({
    ...resolvedConfig.run_id && { runId: resolvedConfig.run_id },
    ...Object.keys(resolvedConfig.contact_channel).length > 0 && {
      contactChannel: resolvedConfig.contact_channel
    }
  });
  server.setRequestHandler(ListToolsRequestSchema, async () => {
    return {
      tools: [
        {
          name: "contact_human",
          description: "Contact a human for assistance",
          inputSchema: {
            type: "object",
            properties: {
              message: { type: "string" }
            },
            required: ["message"]
          }
        }
      ]
    };
  });
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    if (request.params.name === "contact_human") {
      const response = await hl.fetchHumanResponse({
        spec: {
          msg: request.params.arguments?.message
        }
      });
      return {
        content: [
          {
            type: "text",
            text: response
          }
        ]
      };
    }
    throw new McpError(ErrorCode.InvalidRequest, "Invalid tool name");
  });
  const transport = new StdioServerTransport();
  await server.connect(transport);
}
async function startClaudeApprovalsMCPServer() {
  validateAuth();
  const server = new Server(
    {
      name: "humanlayer-claude-approvals",
      version: "1.0.0"
    },
    {
      capabilities: {
        tools: {}
      }
    }
  );
  const resolvedConfig = resolveFullConfig({});
  const hl = humanlayer2({
    ...resolvedConfig.run_id && { runId: resolvedConfig.run_id },
    ...Object.keys(resolvedConfig.contact_channel).length > 0 && {
      contactChannel: resolvedConfig.contact_channel
    }
  });
  server.setRequestHandler(ListToolsRequestSchema, async () => {
    return {
      tools: [
        {
          name: "request_permission",
          description: "Request permission to perform an action",
          inputSchema: {
            type: "object",
            properties: {
              tool_name: { type: "string" },
              input: { type: "object" }
            },
            required: ["tool_name", "input"]
          }
        }
      ]
    };
  });
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    if (request.params.name === "request_permission") {
      const toolName = request.params.arguments?.tool_name;
      if (!toolName) {
        throw new McpError(ErrorCode.InvalidRequest, "Invalid tool name requesting permissions");
      }
      const input = request.params.arguments?.input || {};
      const approvalResult = await hl.fetchHumanApproval({
        spec: {
          fn: toolName,
          kwargs: input
        }
      });
      if (!approvalResult.approved) {
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify({
                behavior: "deny",
                message: approvalResult.comment || "Request denied by human reviewer"
              })
            }
          ]
        };
      }
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify({
              behavior: "allow",
              updatedInput: input
            })
          }
        ]
      };
    }
    throw new McpError(ErrorCode.InvalidRequest, "Invalid tool name");
  });
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

// src/index.ts
import chalk6 from "chalk";
function showAbbreviatedConfig() {
  const configWithSources = resolveConfigWithSources({});
  console.log(`
${chalk6.yellow("Current configuration:")}`);
  console.log(
    `  API Base URL: ${chalk6.cyan(configWithSources.api_base_url.value)} ${chalk6.gray(
      `(${configWithSources.api_base_url.sourceName})`
    )}`
  );
  console.log(
    `  App Base URL: ${chalk6.cyan(configWithSources.app_base_url.value)} ${chalk6.gray(
      `(${configWithSources.app_base_url.sourceName})`
    )}`
  );
  const apiKeyDisplay = configWithSources.api_key?.value ? chalk6.green(maskSensitiveValue(configWithSources.api_key.value)) : chalk6.red(maskSensitiveValue(void 0));
  console.log(`  API Key: ${apiKeyDisplay} ${chalk6.gray(`(${configWithSources.api_key?.sourceName})`)}`);
}
var program = new Command();
async function authenticate(printSelectedProject = false) {
  const config = resolveFullConfig({});
  if (!config.api_key) {
    console.error("Error: No HumanLayer API token found.");
    showAbbreviatedConfig();
    process.exit(1);
  }
  try {
    await getProject(config.api_base_url, config.api_key);
    if (printSelectedProject) {
    }
  } catch (error) {
    console.error(chalk6.red("Authentication failed:"), error);
    showAbbreviatedConfig();
    process.exit(1);
  }
}
program.name("humanlayer").description("HumanLayer, but on your command-line.").version("0.7.0");
var UNPROTECTED_COMMANDS = ["config", "login"];
program.hook("preAction", async (thisCmd, actionCmd) => {
  const getCommandPath = (cmd) => {
    const path2 = [cmd.name()];
    let parent = cmd.parent;
    while (parent && parent.name() !== "humanlayer") {
      path2.unshift(parent.name());
      parent = parent.parent;
    }
    return path2;
  };
  const commandPath = getCommandPath(actionCmd);
  const isUnprotected = commandPath.some((cmd) => UNPROTECTED_COMMANDS.includes(cmd));
  if (!isUnprotected) {
    await authenticate(true);
  }
});
program.command("login").description("Login to HumanLayer and save API token").option("--api-base <url>", "API base URL").option("--app-base <url>", "App base URL").option("--config-file <path>", "Path to config file").action(loginCommand);
program.command("tui").description("Run the HumanLayer Terminal UI").option("--daemon-socket <path>", "Path to daemon socket").option("--config-file <path>", "Path to config file").action(tuiCommand);
program.command("launch <prompt>").description("Launch a new Claude Code session via the daemon").option("-m, --model <model>", "Model to use (opus or sonnet)", "sonnet").option("-w, --working-dir <path>", "Working directory for the session").option("--max-turns <number>", "Maximum number of turns", parseInt).option("--no-approvals", "Disable HumanLayer approvals for high-stakes operations").option("--daemon-socket <path>", "Path to daemon socket").option("--config-file <path>", "Path to config file").action(launchCommand);
var configCommand = program.command("config").description("Configuration management");
configCommand.command("edit").description("Edit configuration file in $EDITOR").option("--config-file <path>", "Path to config file").action((options) => {
  const editor = process.env.EDITOR || "vi";
  const configFile = options.configFile || getDefaultConfigPath();
  spawn3(editor, [configFile], { stdio: "inherit" });
});
configCommand.command("show").description("Show current configuration").option("--config-file <path>", "Path to config file").option("--slack-channel <id>", "Slack channel or user ID").option("--slack-bot-token <token>", "Slack bot token").option("--slack-context <context>", "Context about the Slack channel or user").option("--slack-thread-ts <ts>", "Slack thread timestamp").option("--slack-blocks [boolean]", "Use experimental Slack blocks").option("--email-address <email>", "Email address to contact").option("--email-context <context>", "Context about the email recipient").option("--json", "Output as JSON with masked keys").action(configShowCommand);
program.command("contact_human").description("Contact a human with a message").requiredOption("-m, --message <text>", 'The message to send (use "-" to read from stdin)').option("--slack-channel <id>", "Slack channel or user ID").option("--slack-bot-token <token>", "Slack bot token").option("--slack-context <context>", "Context about the Slack channel or user").option("--slack-thread-ts <ts>", "Slack thread timestamp").option("--slack-blocks [boolean]", "Use experimental Slack blocks", true).option("--email-address <email>", "Email address to contact").option("--email-context <context>", "Context about the email recipient").action(contactHumanCommand);
program.command("ping").description("Check authentication and display current project").action(pingCommand);
var mcpCommand = program.command("mcp").description("MCP server functionality");
mcpCommand.command("serve").description("Start the default MCP server for contact_human functionality").action(startDefaultMCPServer);
mcpCommand.command("claude_approvals").description("Start the Claude approvals MCP server for permission requests").action(startClaudeApprovalsMCPServer);
mcpCommand.command("wrapper").description("Wrap an existing MCP server with human approval functionality (not implemented yet)").action(() => {
  console.log("MCP wrapper functionality is not implemented yet.");
  console.log("This will allow wrapping any existing MCP server with human approval.");
  process.exit(1);
});
mcpCommand.command("inspector").description("Run MCP inspector for debugging MCP servers").argument("[command]", "MCP server command to inspect", "serve").action((command) => {
  const args = ["@modelcontextprotocol/inspector", "node", "dist/index.js", "mcp", command];
  spawn3("npx", args, { stdio: "inherit", cwd: process.cwd() });
});
program.on("command:*", (operands) => {
  console.error(`Unknown command: ${operands[0]}`);
  console.error('Run "humanlayer --help" for available commands');
  process.exit(1);
});
program.action(() => {
  tuiCommand();
});
program.configureOutput({
  writeErr: (str) => {
    if (str.includes("too many arguments")) {
      console.error("Unknown command");
      console.error('Run "humanlayer --help" for available commands');
      process.exit(1);
    } else {
      process.stderr.write(str);
    }
  }
});
program.parse(process.argv);
