import { createRequire as _cjsReq } from 'module'; if(typeof require === 'undefined') var require = _cjsReq(import.meta.url);
import {
  execa
} from "./chunk-6EX6E7WP.js";
import {
  GuildAPIClient,
  loadGlobalConfig,
  loadLocalConfig
} from "./chunk-RJHD6XTV.js";
import {
  createSpinner,
  debug
} from "./chunk-JQRJ4A4S.js";
import {
  esm_default
} from "./chunk-M347HP6M.js";

// src/lib/git.ts
import * as fs from "fs/promises";
import * as path from "path";
var GitError = class extends Error {
  constructor(command, args, stderr, stdout, exitCode, timedOut) {
    const output = stderr.trim() || stdout.trim();
    const baseMessage = timedOut ? `Git command timed out: git ${args.join(" ")}` : `Git command failed: git ${args.join(" ")}`;
    super(output ? `${baseMessage}
${output}` : baseMessage);
    this.command = command;
    this.args = args;
    this.stderr = stderr;
    this.stdout = stdout;
    this.exitCode = exitCode;
    this.timedOut = timedOut;
    this.name = "GitError";
  }
  command;
  args;
  stderr;
  stdout;
  exitCode;
  timedOut;
};
var DEFAULT_TIMEOUT = 3e4;
var NETWORK_TIMEOUT = 12e4;
var NETWORK_COMMANDS = ["clone", "push", "pull", "fetch"];
async function runGit(args, options = {}) {
  const { cwd, timeout: customTimeout, env: extraEnv } = options;
  const isNetworkCommand = args.length > 0 && NETWORK_COMMANDS.includes(args[0]);
  const timeout = customTimeout ?? (isNetworkCommand ? NETWORK_TIMEOUT : DEFAULT_TIMEOUT);
  debug(`Running git command: git ${args.join(" ")} (timeout: ${timeout}ms)`);
  let result;
  try {
    result = await execa("git", args, {
      cwd,
      timeout,
      reject: false,
      env: {
        ...process.env,
        // Prevent git from prompting for credentials - fail fast instead
        GIT_TERMINAL_PROMPT: "0",
        // Prevent SSH from prompting for passwords/passphrases
        GIT_SSH_COMMAND: "ssh -o BatchMode=yes",
        ...extraEnv
      }
    });
  } catch (error) {
    const execaError = error;
    if (execaError.timedOut) {
      throw new GitError(
        "git",
        args,
        String(execaError.stderr || ""),
        String(execaError.stdout || ""),
        null,
        true
      );
    }
    throw error;
  }
  const stdout = String(result.stdout ?? "");
  const stderr = String(result.stderr ?? "");
  const exitCode = result.exitCode ?? 0;
  if (result.timedOut) {
    throw new GitError("git", args, stderr, stdout, null, true);
  }
  if (exitCode !== 0) {
    throw new GitError("git", args, stderr, stdout, exitCode, false);
  }
  return { stdout, stderr, exitCode };
}
function formatGitError(error) {
  const lines = [];
  if (error.timedOut) {
    lines.push("Git command timed out");
    lines.push("");
    lines.push("This usually means git is waiting for input that cannot be provided.");
    lines.push("Common causes:");
    lines.push("  \u2022 GPG signing requires a passphrase - run `gpg --sign` to cache it");
    lines.push("  \u2022 SSH key requires a passphrase - run `ssh-add` to cache it");
    lines.push("  \u2022 Git credentials are needed - configure a credential helper");
  } else {
    lines.push("Git operation failed");
  }
  lines.push("");
  lines.push(`Command: git ${error.args.join(" ")}`);
  if (error.stderr) {
    lines.push("");
    lines.push("Output:");
    lines.push(error.stderr.trim());
  } else if (error.stdout) {
    lines.push("");
    lines.push("Output:");
    lines.push(error.stdout.trim());
  }
  if (error.exitCode !== null) {
    lines.push("");
    lines.push(`Exit code: ${error.exitCode}`);
  }
  return lines.join("\n");
}
var PRE_PUSH_HOOK = `#!/bin/sh
if [ "$GUILD_AGENT_SAVE" != "1" ]; then
  echo "Please use \\\`guild agent save\\\` to push."
  exit 1
fi
exit 0
`;
async function installPrePushHook(targetDir) {
  const hooksDir = path.join(targetDir, ".git", "hooks");
  await fs.mkdir(hooksDir, { recursive: true });
  await fs.writeFile(path.join(hooksDir, "pre-push"), PRE_PUSH_HOOK, {
    mode: 493
  });
}

// src/lib/agent-helpers.ts
import { createHash } from "crypto";
import * as fs2 from "fs/promises";
import * as path2 from "path";

// src/lib/owner-helpers.ts
async function lookupOwner(client, val) {
  const me = await client.get("/me");
  if (me.id === val || me.name === val) {
    return { id: me.id, name: me.name, type: "user" };
  }
  const orgs = await client.fetchAll("/me/organizations");
  const org = orgs.find((o) => o.id === val || o.name === val);
  if (org) {
    return { id: org.id, name: org.name, type: "organization" };
  }
  return void 0;
}
async function resolveOwnerId(options) {
  const { ownerFlag, client, interactive, requireExplicitOwner = false } = options;
  if (ownerFlag) {
    debug("Using owner from --owner flag: %s", ownerFlag);
    const match = await lookupOwner(client, ownerFlag);
    if (match) return match;
    return { id: ownerFlag, name: ownerFlag, type: "organization" };
  }
  if (process.env.GUILD_OWNER_ID) {
    debug("Using owner from GUILD_OWNER_ID env var: %s", process.env.GUILD_OWNER_ID);
    const match = await lookupOwner(client, process.env.GUILD_OWNER_ID);
    if (match) return match;
    return {
      id: process.env.GUILD_OWNER_ID,
      name: process.env.GUILD_OWNER_ID,
      type: "organization"
    };
  }
  const globalConfig = await loadGlobalConfig();
  if (globalConfig?.default_owner) {
    debug("Using owner from global config: %s", globalConfig.default_owner);
    const match = await lookupOwner(client, globalConfig.default_owner);
    if (match) return match;
    const name = globalConfig.default_owner_name || globalConfig.default_owner;
    return { id: globalConfig.default_owner, name, type: "organization" };
  }
  const me = await client.get("/me");
  const orgs = await client.fetchAll("/me/organizations");
  if (orgs.length === 0 && !interactive) {
    debug("No organizations found, using current user as owner");
    return { id: me.id, name: me.name, type: "user" };
  }
  if (orgs.length > 0 && !interactive) {
    if (requireExplicitOwner) {
      debug("Non-interactive mode with multiple accounts available, requiring --owner");
      throw new Error(
        "Owner is required in non-interactive mode when multiple accounts are available.\n\nUse --owner <name-or-id> to specify an owner, or set a default:\n  guild config set default_owner <name-or-id>"
      );
    }
    debug("Multiple accounts available, defaulting to current user");
    return { id: me.id, name: me.name, type: "user" };
  }
  const choices = [
    {
      name: `${me.name} (personal)`,
      value: { id: me.id, name: me.name, type: "user" }
    },
    ...orgs.map((org) => ({
      name: org.name,
      value: { id: org.id, name: org.name, type: "organization" }
    }))
  ];
  const { owner } = await esm_default.prompt([
    {
      type: "list",
      name: "owner",
      message: "Select owner for this agent:",
      choices
    }
  ]);
  return owner;
}

// src/lib/polling.ts
async function pollUntilComplete(options) {
  const {
    endpoint,
    isComplete,
    message = "Waiting for operation to complete...",
    maxAttempts = 60,
    delayMs = 1e3,
    successMessage = "Operation complete",
    timeoutMessage = "Operation timed out",
    onPoll
  } = options;
  const client = new GuildAPIClient();
  const spinner = createSpinner(message);
  spinner.start();
  let attempts = 0;
  let lastResponse;
  for (attempts = 1; attempts <= maxAttempts; attempts++) {
    await new Promise((resolve) => setTimeout(resolve, delayMs));
    try {
      const response = await client.get(endpoint);
      lastResponse = response;
      if (isComplete(response)) {
        spinner.succeed(successMessage);
        return {
          success: true,
          response,
          attempts
        };
      }
      let spinnerText = null;
      if (onPoll) {
        try {
          spinnerText = await onPoll(response, attempts);
        } catch {
        }
      }
      spinner.text = spinnerText ?? `${message} (${attempts}/${maxAttempts})`;
    } catch {
      if (attempts === maxAttempts) {
        spinner.warn("Could not verify operation status");
        return {
          success: false,
          response: lastResponse,
          attempts
        };
      }
    }
  }
  spinner.warn(timeoutMessage);
  return {
    success: false,
    response: lastResponse,
    attempts
  };
}

// src/lib/agent-helpers.ts
var BuildTimeoutError = class extends Error {
  constructor(message = "The agent version build timed out or failed to report status.") {
    super(message);
    this.name = "BuildTimeoutError";
  }
};
var BuildFailedError = class extends Error {
  failedSteps;
  constructor(failedSteps) {
    const stepSummary = failedSteps.length > 0 ? failedSteps.map((s) => `Step "${s.name}" failed:${s.content ? `
${s.content}` : ""}`).join("\n") : "No failed steps found. Check validation logs for details.";
    super(`Build failed

${stepSummary}

Fix the issues and retry.`);
    this.name = "BuildFailedError";
    this.failedSteps = failedSteps;
  }
};
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
async function resolveAgentRef(client, identifier) {
  if (identifier.includes("~") || UUID_RE.test(identifier)) {
    return identifier;
  }
  if (identifier.includes("/")) {
    return identifier.replace("/", "~");
  }
  const owner = await resolveOwnerId({ client, interactive: false });
  return `${owner.name}~${identifier}`;
}
async function getAgentId(agentIdArg, cwd = process.cwd()) {
  if (agentIdArg) {
    return { agentId: agentIdArg };
  }
  const config = await loadLocalConfig(cwd);
  if (!config) {
    console.error("Error: No agent ID provided and not in an agent directory");
    console.error("");
    console.error("Either provide an agent ID:");
    console.error("  guild agent <command> <agent-id>");
    console.error("");
    console.error("Or run from an agent directory with guild.json:");
    console.error("  cd <agent-directory>");
    console.error("  guild agent <command>");
    console.error("");
    console.error("To initialize an agent directory:");
    console.error("  guild agent init --name my-agent");
    console.error("");
    console.error("Or clone an existing agent:");
    console.error("  guild agent clone <agent-id>");
    process.exit(1);
  }
  if (!config.agent_id) {
    console.error("Error: guild.json is missing agent_id field");
    console.error("");
    console.error("Your guild.json file appears to be corrupted.");
    console.error("Expected format:");
    console.error("  {");
    console.error('    "agent_id": "...",');
    console.error('    "name": "..."');
    console.error("  }");
    process.exit(1);
  }
  return { agentId: config.agent_id, config };
}
var REQUIRED_AGENT_FILES = ["agent.ts", "package.json"];
async function readAgentFiles(cwd) {
  const { stdout } = await runGit(
    ["ls-files", "--cached", "--others", "--exclude-standard"],
    { cwd }
  );
  const gitFiles = stdout.split("\n").filter((f) => f.trim().length > 0);
  const relevantFiles = gitFiles.filter((f) => !f.startsWith(".guild/"));
  const files = [];
  for (const filePath of relevantFiles) {
    const fullPath = path2.join(cwd, filePath);
    const content = await fs2.readFile(fullPath, "utf-8");
    files.push({ path: filePath, content });
  }
  const filePaths = files.map((f) => f.path);
  const missing = REQUIRED_AGENT_FILES.filter(
    (req) => !filePaths.some((fp) => fp === req || fp.endsWith(`/${req}`))
  );
  if (missing.length > 0) {
    throw new Error(`Missing required files: ${missing.join(", ")}`);
  }
  return files;
}
var CACHE_DIR = path2.join(".guild", "cache");
var CACHE_FILE = "last-ephemeral.json";
function hashAgentFiles(files) {
  const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
  const hash = createHash("sha256");
  for (const file of sorted) {
    hash.update(file.path);
    hash.update("\0");
    hash.update(file.content);
    hash.update("\0");
  }
  return hash.digest("hex");
}
async function readEphemeralCache(cwd) {
  try {
    const cachePath = path2.join(cwd, CACHE_DIR, CACHE_FILE);
    const raw = await fs2.readFile(cachePath, "utf-8");
    const parsed = JSON.parse(raw);
    if (typeof parsed.hash === "string" && typeof parsed.version_id === "string") {
      return { hash: parsed.hash, version_id: parsed.version_id };
    }
    return null;
  } catch {
    return null;
  }
}
async function writeEphemeralCache(cwd, cache) {
  const dir = path2.join(cwd, CACHE_DIR);
  await fs2.mkdir(dir, { recursive: true });
  await fs2.writeFile(path2.join(dir, CACHE_FILE), JSON.stringify(cache, null, 2) + "\n");
}
async function getOrCreateEphemeral(client, agentId, files, cwd, summary, options) {
  const hash = hashAgentFiles(files);
  if (options?.noCache) {
    const version2 = await client.post(`/agents/${agentId}/versions`, {
      files,
      summary,
      version_type: "EPHEMERAL"
    });
    return { version: version2, cached: false, hash };
  }
  const cache = await readEphemeralCache(cwd);
  if (cache && cache.hash === hash) {
    try {
      const version2 = await client.get(
        `/versions/${cache.version_id}`
      );
      if (version2.validation_status === "PASSED") {
        return { version: version2, cached: true, hash };
      }
    } catch {
    }
  }
  const version = await client.post(`/agents/${agentId}/versions`, {
    files,
    summary,
    version_type: "EPHEMERAL"
  });
  return { version, cached: false, hash };
}
async function buildEphemeralVersion(client, agentId, files, cwd, summary, options) {
  const {
    version: initial,
    cached,
    hash
  } = await getOrCreateEphemeral(client, agentId, files, cwd, summary, options);
  if (cached) {
    return { version: initial, cached: true };
  }
  const pollResult = await pollUntilComplete({
    resourceId: initial.id,
    endpoint: `/versions/${initial.id}`,
    isComplete: (r) => r.validation_status !== "PENDING" && r.validation_status !== "RUNNING",
    message: "Building...",
    successMessage: "Build finished",
    timeoutMessage: "Build timed out",
    maxAttempts: 120,
    delayMs: 1e3
  });
  if (!pollResult.success || !pollResult.response) {
    throw new BuildTimeoutError();
  }
  const version = pollResult.response;
  if (version.validation_status === "PASSED") {
    await writeEphemeralCache(cwd, { hash, version_id: version.id });
  }
  if (version.validation_status === "FAILED") {
    let failedSteps = [];
    try {
      const stepsResponse = await client.get(
        `/versions/${version.id}/validation/steps`
      );
      failedSteps = stepsResponse.steps.filter((step) => step.status === "FAILED").map((step) => ({ name: step.name, content: step.content ?? void 0 }));
    } catch {
    }
    throw new BuildFailedError(failedSteps);
  }
  return { version, cached: false };
}
var BundleNotFoundError = class extends Error {
  constructor(filePath) {
    super(`Bundle file not found: ${filePath}`);
    this.name = "BundleNotFoundError";
  }
};
async function buildBundledVersion(client, agentId, bundlePath, cwd, summary) {
  try {
    await fs2.access(bundlePath);
  } catch {
    throw new BundleNotFoundError(bundlePath);
  }
  const bundle = (await fs2.readFile(bundlePath, "utf-8")).trim();
  let files = [];
  try {
    files = await readAgentFiles(cwd);
  } catch {
  }
  const initial = await client.post(`/agents/${agentId}/versions`, {
    version_type: "EPHEMERAL",
    bundle,
    encoding: "gzip;base64",
    files,
    summary
  });
  const pollResult = await pollUntilComplete({
    resourceId: initial.id,
    endpoint: `/versions/${initial.id}`,
    isComplete: (r) => r.validation_status !== "PENDING" && r.validation_status !== "RUNNING",
    message: "Validating bundle...",
    successMessage: "Bundle validated",
    timeoutMessage: "Bundle validation timed out",
    maxAttempts: 120,
    delayMs: 1e3
  });
  if (!pollResult.success || !pollResult.response) {
    throw new BuildTimeoutError("Bundle validation timed out.");
  }
  const version = pollResult.response;
  if (version.validation_status === "FAILED") {
    let failedSteps = [];
    try {
      const stepsResponse = await client.get(
        `/versions/${version.id}/validation/steps`
      );
      failedSteps = stepsResponse.steps.filter((step) => step.status === "FAILED").map((step) => ({ name: step.name, content: step.content ?? void 0 }));
    } catch {
    }
    throw new BuildFailedError(failedSteps);
  }
  return { version };
}

export {
  GitError,
  runGit,
  formatGitError,
  installPrePushHook,
  lookupOwner,
  resolveOwnerId,
  pollUntilComplete,
  BuildTimeoutError,
  BuildFailedError,
  resolveAgentRef,
  getAgentId,
  readAgentFiles,
  buildEphemeralVersion,
  BundleNotFoundError,
  buildBundledVersion
};
//# sourceMappingURL=chunk-EQUW4M5A.js.map
