// Force strict mode and setup for ESM
"use strict";
import {
  require_undici
} from "./chunk-54YZOP7P.js";
import {
  So,
  isGitRepository
} from "./chunk-33JSFFSK.js";
import {
  createDebugLogger
} from "./chunk-22MNNNXE.js";
import {
  init_esbuild_shims
} from "./chunk-A4BMJM77.js";
import {
  __name,
  __toESM
} from "./chunk-J2S4EL5Y.js";

// packages/cli/src/utils/installationInfo.ts
init_esbuild_shims();
import * as fs from "node:fs";
import * as path from "node:path";
import * as childProcess from "node:child_process";
var debugLogger = createDebugLogger("INSTALLATION_INFO");
var STANDALONE_UNIX_INSTALLER = "https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.sh";
var STANDALONE_WINDOWS_INSTALLER = "https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.ps1";
function getInstallationInfo(projectRoot, isAutoUpdateEnabled) {
  const cliPath = process.argv[1];
  if (!cliPath) {
    return { packageManager: "unknown" /* UNKNOWN */, isGlobal: false };
  }
  try {
    const realPath = fs.realpathSync(cliPath).replace(/\\/g, "/");
    const normalizedProjectRoot = projectRoot?.replace(/\\/g, "/");
    const isGit = isGitRepository(process.cwd());
    if (isGit && normalizedProjectRoot && isSamePathOrInside(realPath, normalizedProjectRoot) && !realPath.includes("/node_modules/")) {
      return {
        packageManager: "unknown" /* UNKNOWN */,
        // Not managed by a package manager in this sense
        isGlobal: false,
        updateMessage: 'Running from a local git clone. Please update with "git pull".'
      };
    }
    if (realPath.includes("/.npm/_npx") || realPath.includes("/npm/_npx")) {
      return {
        packageManager: "npx" /* NPX */,
        isGlobal: false,
        updateMessage: "Running via npx, update not applicable."
      };
    }
    if (realPath.includes("/.pnpm/_pnpx")) {
      return {
        packageManager: "pnpx" /* PNPX */,
        isGlobal: false,
        updateMessage: "Running via pnpx, update not applicable."
      };
    }
    const standaloneInfo = getStandaloneInstallInfo(
      realPath,
      isAutoUpdateEnabled
    );
    if (standaloneInfo) {
      return standaloneInfo;
    }
    if (process.platform === "darwin") {
      try {
        childProcess.execSync('brew list -1 | grep -q "^qwen-code$"', {
          stdio: "ignore"
        });
        return {
          packageManager: "homebrew" /* HOMEBREW */,
          isGlobal: true,
          updateMessage: 'Installed via Homebrew. Please update with "brew upgrade".'
        };
      } catch (_error) {
      }
    }
    if (realPath.includes("/.pnpm/global")) {
      const updateCommand2 = "pnpm add -g @qwen-code/qwen-code@latest";
      return {
        packageManager: "pnpm" /* PNPM */,
        isGlobal: true,
        updateCommand: updateCommand2,
        updateMessage: isAutoUpdateEnabled ? "Installed with pnpm. Attempting to automatically update now..." : `Please run ${updateCommand2} to update`
      };
    }
    if (realPath.includes("/.yarn/global")) {
      const updateCommand2 = "yarn global add @qwen-code/qwen-code@latest";
      return {
        packageManager: "yarn" /* YARN */,
        isGlobal: true,
        updateCommand: updateCommand2,
        updateMessage: isAutoUpdateEnabled ? "Installed with yarn. Attempting to automatically update now..." : `Please run ${updateCommand2} to update`
      };
    }
    if (realPath.includes("/.bun/install/cache")) {
      return {
        packageManager: "bunx" /* BUNX */,
        isGlobal: false,
        updateMessage: "Running via bunx, update not applicable."
      };
    }
    if (realPath.includes("/.bun/bin")) {
      const updateCommand2 = "bun add -g @qwen-code/qwen-code@latest";
      return {
        packageManager: "bun" /* BUN */,
        isGlobal: true,
        updateCommand: updateCommand2,
        updateMessage: isAutoUpdateEnabled ? "Installed with bun. Attempting to automatically update now..." : `Please run ${updateCommand2} to update`
      };
    }
    if (normalizedProjectRoot && isSamePathOrInside(realPath, `${normalizedProjectRoot}/node_modules`)) {
      let pm = "npm" /* NPM */;
      if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) {
        pm = "yarn" /* YARN */;
      } else if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) {
        pm = "pnpm" /* PNPM */;
      } else if (fs.existsSync(path.join(projectRoot, "bun.lockb"))) {
        pm = "bun" /* BUN */;
      }
      return {
        packageManager: pm,
        isGlobal: false,
        updateMessage: "Locally installed. Please update via your project's package.json."
      };
    }
    const npmPackageDir = path.dirname(path.dirname(realPath));
    let npmPrefixWritable = false;
    try {
      fs.accessSync(npmPackageDir, fs.constants.W_OK);
      npmPrefixWritable = true;
    } catch {
    }
    if (!npmPrefixWritable) {
      return {
        packageManager: "npm" /* NPM */,
        isGlobal: true,
        updateMessage: "Update requires sudo. Please run: sudo npm install -g @qwen-code/qwen-code@latest"
      };
    }
    const updateCommand = "npm install -g @qwen-code/qwen-code@latest";
    return {
      packageManager: "npm" /* NPM */,
      isGlobal: true,
      updateCommand,
      updateMessage: isAutoUpdateEnabled ? "Installed with npm. Attempting to automatically update now..." : `Please run ${updateCommand} to update`
    };
  } catch (error) {
    debugLogger.error("Failed to detect installation info:", error);
    return { packageManager: "unknown" /* UNKNOWN */, isGlobal: false };
  }
}
__name(getInstallationInfo, "getInstallationInfo");
function stripTrailingSlashes(value) {
  return value.replace(/\/+$/, "") || "/";
}
__name(stripTrailingSlashes, "stripTrailingSlashes");
function isSamePathOrInside(candidate, parent) {
  const normalizedCandidate = stripTrailingSlashes(candidate);
  const normalizedParent = stripTrailingSlashes(parent);
  if (normalizedParent === "/") {
    return normalizedCandidate === "/" || normalizedCandidate.startsWith("/");
  }
  return normalizedCandidate === normalizedParent || normalizedCandidate.startsWith(`${normalizedParent}/`);
}
__name(isSamePathOrInside, "isSamePathOrInside");
function getStandaloneInstallInfo(realPath, isAutoUpdateEnabled) {
  const installDir = standaloneInstallDirForCliPath(realPath);
  if (!installDir || !isStandaloneInstallDir(installDir)) {
    return null;
  }
  const updateCommand = process.platform === "win32" ? `powershell -ExecutionPolicy Bypass -c "irm ${STANDALONE_WINDOWS_INSTALLER} | iex"` : `curl -fsSL ${STANDALONE_UNIX_INSTALLER} | bash`;
  return {
    packageManager: "standalone" /* STANDALONE */,
    isGlobal: true,
    isStandalone: true,
    standaloneDir: installDir,
    updateMessage: isAutoUpdateEnabled ? "Standalone install detected. Attempting to automatically update now..." : `Standalone install detected. Please rerun the standalone installer to update: ${updateCommand}`
  };
}
__name(getStandaloneInstallInfo, "getStandaloneInstallInfo");
function standaloneInstallDirForCliPath(realPath) {
  const normalized = realPath.replace(/\\/g, "/");
  const suffix = "/lib/cli.js";
  if (!normalized.endsWith(suffix)) {
    return null;
  }
  return realPath.slice(0, -suffix.length);
}
__name(standaloneInstallDirForCliPath, "standaloneInstallDirForCliPath");
function isStandaloneInstallDir(installDir) {
  try {
    const manifestPath = path.join(installDir, "manifest.json");
    if (!fs.existsSync(manifestPath)) {
      return false;
    }
    const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
    if (manifest.name !== "@qwen-code/qwen-code" || typeof manifest.target !== "string" || !isStandaloneTargetForCurrentPlatform(manifest.target)) {
      return false;
    }
    const qwenBin = process.platform === "win32" ? path.join(installDir, "bin", "qwen.cmd") : path.join(installDir, "bin", "qwen");
    const nodeBin = process.platform === "win32" ? path.join(installDir, "node", "node.exe") : path.join(installDir, "node", "bin", "node");
    return fs.existsSync(qwenBin) && fs.existsSync(nodeBin) && isStandaloneRuntimeFile(qwenBin) && isStandaloneRuntimeFile(nodeBin);
  } catch (err) {
    debugLogger.error("Standalone detection failed:", installDir, err);
    return false;
  }
}
__name(isStandaloneInstallDir, "isStandaloneInstallDir");
function isStandaloneTargetForCurrentPlatform(target) {
  switch (process.platform) {
    case "darwin":
      return /^darwin-(arm64|x64)$/.test(target);
    case "linux":
      return /^linux-(arm64|x64)$/.test(target);
    case "win32":
      return /^win-(arm64|x64)$/.test(target);
    default:
      return false;
  }
}
__name(isStandaloneTargetForCurrentPlatform, "isStandaloneTargetForCurrentPlatform");
function isStandaloneRuntimeFile(filePath) {
  const stats = fs.lstatSync(filePath);
  if (!stats.isFile() || stats.isSymbolicLink()) {
    return false;
  }
  return process.platform === "win32" || (stats.mode & 73) !== 0;
}
__name(isStandaloneRuntimeFile, "isStandaloneRuntimeFile");

// packages/cli/src/utils/standalone-update.ts
init_esbuild_shims();
var import_undici = __toESM(require_undici(), 1);
import { createHash } from "node:crypto";
import * as fs2 from "node:fs";
import * as os from "node:os";
import * as path2 from "node:path";
import { Readable, Transform } from "node:stream";
import { spawn, execFile } from "node:child_process";
import { pipeline } from "node:stream/promises";

// packages/cli/src/utils/standalone-update-verify.ts
init_esbuild_shims();
import { createPublicKey, verify } from "node:crypto";
var RELEASE_PUBLIC_KEY_DER_B64 = "MCowBQYDK2VwAyEAr9WRFLDauibZQKCe1oKfuFZn6zRMaEkD5+KVqN6mKM4=";
function verifySignature(sha256sumsContent, signatureBase64) {
  const key = createPublicKey({
    key: Buffer.from(RELEASE_PUBLIC_KEY_DER_B64, "base64"),
    format: "der",
    type: "spki"
  });
  const signature = Buffer.from(signatureBase64, "base64");
  if (signature.length !== 64) {
    throw new Error(
      `Invalid signature length: expected 64 bytes, got ${signature.length}`
    );
  }
  const valid = verify(null, Buffer.from(sha256sumsContent), key, signature);
  if (!valid) {
    throw new Error(
      "SHA256SUMS signature verification failed \u2014 possible tampering detected"
    );
  }
}
__name(verifySignature, "verifySignature");

// packages/cli/src/utils/standalone-update.ts
var debugLogger2 = createDebugLogger("STANDALONE_UPDATE");
var OSS_BASE = "https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/releases/qwen-code";
var GITHUB_BASE = "https://github.com/QwenLM/qwen-code/releases/download";
var FETCH_TIMEOUT_MS = 3e4;
var ARCHIVE_TIMEOUT_MS = 3e5;
var VALID_TARGETS = /* @__PURE__ */ new Set([
  "darwin-arm64",
  "darwin-x64",
  "linux-arm64",
  "linux-x64",
  "win-x64"
]);
var SEMVER_RE = /^v?\d+\.\d+\.\d+(-[\w.]+)?$/;
function normalizeVersion(version) {
  if (!SEMVER_RE.test(version)) {
    throw new Error(`Invalid version format: ${version}`);
  }
  return version.startsWith("v") ? version : `v${version}`;
}
__name(normalizeVersion, "normalizeVersion");
function validateTarget(target) {
  if (!VALID_TARGETS.has(target)) {
    throw new Error(`Unknown target: ${target}`);
  }
}
__name(validateTarget, "validateTarget");
function archiveFilename(target) {
  const ext = target.startsWith("win") ? "zip" : "tar.gz";
  return `qwen-code-${target}.${ext}`;
}
__name(archiveFilename, "archiveFilename");
function escapePS(s) {
  return s.replace(/'/g, "''");
}
__name(escapePS, "escapePS");
async function tryFetch(url, timeoutMs = FETCH_TIMEOUT_MS) {
  try {
    const res = await (0, import_undici.fetch)(url, {
      signal: AbortSignal.timeout(timeoutMs)
    });
    if (res.ok) return { response: res };
    await res.body?.cancel().catch(() => {
    });
    return { error: new Error(`HTTP ${res.status} ${res.statusText}`) };
  } catch (err) {
    const error = err instanceof Error ? err : new Error(String(err));
    debugLogger2.debug(`Fetch failed for ${url}: ${error.message}`);
    return { error };
  }
}
__name(tryFetch, "tryFetch");
async function downloadWithFallback(versionPath, filename, timeoutMs = FETCH_TIMEOUT_MS) {
  const ossUrl = `${OSS_BASE}/${versionPath}/${filename}`;
  const ossResult = await tryFetch(ossUrl, timeoutMs);
  if (ossResult.response) return ossResult.response;
  const ghUrl = `${GITHUB_BASE}/${versionPath}/${filename}`;
  const ghResult = await tryFetch(ghUrl, timeoutMs);
  if (ghResult.response) return ghResult.response;
  throw new Error(
    `Failed to download ${filename}: OSS (${ossResult.error?.message ?? "unknown"}), GitHub (${ghResult.error?.message ?? "unknown"})`
  );
}
__name(downloadWithFallback, "downloadWithFallback");
async function verifyChecksum(actualHash, filename, versionPath) {
  const response = await downloadWithFallback(versionPath, "SHA256SUMS");
  const text = await response.text();
  const requireSig = process.env["QWEN_REQUIRE_SIGNATURE"] === "1";
  let sigResponse;
  try {
    sigResponse = await downloadWithFallback(versionPath, "SHA256SUMS.sig");
  } catch (err) {
    debugLogger2.debug("SHA256SUMS.sig not available:", err);
  }
  if (sigResponse) {
    const sigContent = await sigResponse.text();
    verifySignature(text, sigContent.trim());
    debugLogger2.info("SHA256SUMS signature verified.");
  } else if (requireSig) {
    throw new Error(
      "SHA256SUMS.sig not found and QWEN_REQUIRE_SIGNATURE=1 is set"
    );
  } else {
    debugLogger2.warn(
      "SHA256SUMS.sig not available \u2014 update integrity relies on SHA256 checksum only. Set QWEN_REQUIRE_SIGNATURE=1 to enforce signature verification."
    );
  }
  const expectedLine = text.split("\n").find((line) => {
    const parts = line.trim().split(/\s+/);
    if (parts.length < 2) return false;
    const name = parts[parts.length - 1].replace(/^\*/, "");
    return name === filename;
  });
  if (!expectedLine) {
    throw new Error(`No checksum found for ${filename} in SHA256SUMS`);
  }
  const expectedHash = expectedLine.trim().split(/\s+/)[0];
  if (actualHash !== expectedHash) {
    throw new Error(
      `Checksum mismatch: expected ${expectedHash}, got ${actualHash}`
    );
  }
}
__name(verifyChecksum, "verifyChecksum");
var MAX_DOWNLOAD_BYTES = 512 * 1024 * 1024;
async function downloadToFile(versionPath, filename, destPath) {
  const response = await downloadWithFallback(
    versionPath,
    filename,
    ARCHIVE_TIMEOUT_MS
  );
  const body = response.body;
  if (!body) throw new Error("Empty response body");
  const contentLength = response.headers.get("content-length");
  if (contentLength && parseInt(contentLength, 10) > MAX_DOWNLOAD_BYTES) {
    await body.cancel().catch(() => {
    });
    throw new Error(
      `Download too large: ${contentLength} bytes exceeds ${MAX_DOWNLOAD_BYTES} limit`
    );
  }
  const hash = createHash("sha256");
  let bytesWritten = 0;
  const dest = fs2.createWriteStream(destPath);
  const sizeGuard = new Transform({
    transform(chunk, _encoding, callback) {
      bytesWritten += chunk.length;
      if (bytesWritten > MAX_DOWNLOAD_BYTES) {
        callback(
          new Error(`Download exceeded ${MAX_DOWNLOAD_BYTES} byte limit`)
        );
      } else {
        hash.update(chunk);
        callback(null, chunk);
      }
    }
  });
  await pipeline(Readable.fromWeb(body), sizeGuard, dest);
  return hash.digest("hex");
}
__name(downloadToFile, "downloadToFile");
function validateExtractedPaths(resolvedDest) {
  const entries = fs2.readdirSync(resolvedDest, {
    recursive: true,
    withFileTypes: true
  });
  for (const entry of entries) {
    const fullPath = path2.join(
      String(entry.parentPath || entry.path),
      entry.name
    );
    const resolved = fs2.realpathSync(fullPath);
    if (!resolved.startsWith(resolvedDest + path2.sep) && resolved !== resolvedDest) {
      fs2.rmSync(resolvedDest, { recursive: true, force: true });
      throw new Error(
        `Path traversal detected in archive: ${entry.name} resolves to ${resolved}`
      );
    }
  }
}
__name(validateExtractedPaths, "validateExtractedPaths");
function isSafeTarEntryPath(entryPath) {
  if (entryPath.length === 0) return false;
  if (path2.posix.isAbsolute(entryPath) || path2.win32.isAbsolute(entryPath)) {
    return false;
  }
  return !entryPath.split(/[\\/]+/).includes("..");
}
__name(isSafeTarEntryPath, "isSafeTarEntryPath");
async function extractArchive(archivePath, destDir, target) {
  fs2.mkdirSync(destDir, { recursive: true });
  if (target.startsWith("win")) {
    await new Promise((resolve2, reject) => {
      const ps = spawn(
        "powershell.exe",
        [
          "-NoProfile",
          "-Command",
          `Expand-Archive -Path '${escapePS(archivePath)}' -DestinationPath '${escapePS(destDir)}' -Force`
        ],
        { stdio: "ignore" }
      );
      ps.on(
        "close",
        (code) => code === 0 ? resolve2() : reject(new Error(`Expand-Archive exited with code ${code}`))
      );
      ps.on("error", reject);
    });
    const resolvedDest = fs2.realpathSync(destDir);
    validateExtractedPaths(resolvedDest);
  } else {
    const resolvedDest = path2.resolve(destDir);
    await So({
      file: archivePath,
      cwd: destDir,
      preservePaths: false,
      filter: /* @__PURE__ */ __name((p, entry) => {
        if (!isSafeTarEntryPath(p)) return false;
        if ("type" in entry && entry.type === "SymbolicLink" && "linkpath" in entry) {
          const linkTarget = path2.resolve(
            resolvedDest,
            path2.dirname(p),
            String(entry.linkpath)
          );
          if (!linkTarget.startsWith(resolvedDest + path2.sep) && linkTarget !== resolvedDest) {
            return false;
          }
        }
        return true;
      }, "filter")
    });
    validateExtractedPaths(fs2.realpathSync(destDir));
  }
}
__name(extractArchive, "extractArchive");
function spawnAndCapture(command, args, timeoutMs) {
  return new Promise((resolve2, reject) => {
    let settled = false;
    const child = execFile(
      command,
      args,
      { timeout: timeoutMs },
      (err, out, stderr) => {
        if (settled) return;
        settled = true;
        if (err && ("killed" in err && err.killed || "signal" in err && err.signal)) {
          reject(new Error("Smoke test timed out"));
          return;
        }
        if (err) {
          const exitCode = "code" in err && typeof err.code === "number" ? err.code : 1;
          resolve2({ exitCode, stdout: out || "", stderr: stderr || "" });
          return;
        }
        resolve2({ exitCode: 0, stdout: out || "", stderr: stderr || "" });
      }
    );
    child.on("error", (e) => {
      if (settled) return;
      settled = true;
      reject(e);
    });
  });
}
__name(spawnAndCapture, "spawnAndCapture");
async function smokeTest(newInstallDir, target) {
  const resolvedInstallDir = path2.resolve(newInstallDir);
  const nodeBin = target.startsWith("win") ? path2.join(resolvedInstallDir, "node", "node.exe") : path2.join(resolvedInstallDir, "node", "bin", "node");
  const cliBin = path2.join(resolvedInstallDir, "lib", "cli.js");
  if (!fs2.existsSync(nodeBin)) {
    throw new Error(`Smoke test failed: node binary not found at ${nodeBin}`);
  }
  if (!fs2.existsSync(cliBin)) {
    throw new Error(`Smoke test failed: cli.js not found at ${cliBin}`);
  }
  const { exitCode, stdout, stderr } = await spawnAndCapture(
    nodeBin,
    [cliBin, "--version"],
    1e4
  );
  if (exitCode !== 0) {
    const detail = stderr.trim() ? `: ${stderr.trim()}` : "";
    throw new Error(
      `Smoke test failed: new binary exited with code ${exitCode}${detail}`
    );
  }
  const version = stdout.trim();
  if (!SEMVER_RE.test(version)) {
    throw new Error(
      `Smoke test failed: unexpected version output "${version}"`
    );
  }
  debugLogger2.info(`Smoke test passed: ${version}`);
}
__name(smokeTest, "smokeTest");
function acquireLock(lockPath) {
  try {
    fs2.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
    return true;
  } catch {
    try {
      const pidStr = fs2.readFileSync(lockPath, "utf-8").trim();
      const pid = parseInt(pidStr, 10);
      if (Number.isNaN(pid) || !isProcessAlive(pid)) {
        fs2.unlinkSync(lockPath);
        try {
          fs2.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
          return true;
        } catch {
          return false;
        }
      }
    } catch {
    }
    return false;
  }
}
__name(acquireLock, "acquireLock");
function releaseLock(lockPath) {
  try {
    fs2.unlinkSync(lockPath);
  } catch {
  }
}
__name(releaseLock, "releaseLock");
function cleanupEmptyStandaloneDir(standaloneDir) {
  const manifestPath = path2.join(standaloneDir, "manifest.json");
  if (fs2.existsSync(standaloneDir) && !fs2.existsSync(manifestPath)) {
    fs2.rmSync(standaloneDir, { recursive: true, force: true });
  }
}
__name(cleanupEmptyStandaloneDir, "cleanupEmptyStandaloneDir");
var UNSAFE_SHELL_CHARS = /["`$\\;\n\r]/;
var UNSAFE_CMD_CHARS = /[&|<>^%!"`\n\r]/;
function assertSafeForShellEmbed(p, context) {
  if (UNSAFE_SHELL_CHARS.test(p)) {
    throw new Error(
      `${context} contains characters unsafe for shell embedding: ${p}`
    );
  }
}
__name(assertSafeForShellEmbed, "assertSafeForShellEmbed");
function isProcessAlive(pid) {
  try {
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}
__name(isProcessAlive, "isProcessAlive");
function atomicReplace(standaloneDir, newDir, lockPath) {
  const oldDir = `${standaloneDir}.old`;
  const pendingDir = `${standaloneDir}.new`;
  if (fs2.existsSync(oldDir)) {
    fs2.rmSync(oldDir, { recursive: true, force: true });
  }
  if (os.platform() === "win32") {
    if (UNSAFE_CMD_CHARS.test(standaloneDir) || UNSAFE_CMD_CHARS.test(oldDir) || UNSAFE_CMD_CHARS.test(pendingDir)) {
      throw new Error(
        "Installation path contains characters unsafe for deferred update script"
      );
    }
    if (fs2.existsSync(pendingDir)) {
      fs2.rmSync(pendingDir, { recursive: true, force: true });
    }
    fs2.renameSync(newDir, pendingDir);
    const lockFile = lockPath;
    const logFile = path2.join(path2.dirname(standaloneDir), "qwen-update.log");
    const script = [
      "@echo off",
      "set /a TRIES=0",
      ":wait",
      "set /a TRIES+=1",
      "if %TRIES% GTR 30 goto proceed",
      `tasklist /FI "PID eq ${process.pid}" 2>nul | find "${process.pid}" >nul && (timeout /t 1 >nul & goto wait)`,
      ":proceed",
      `echo [%DATE% %TIME%] starting swap >> "${logFile}"`,
      `move /Y "${standaloneDir}" "${oldDir}"`,
      "if errorlevel 1 goto move1_failed",
      `move /Y "${pendingDir}" "${standaloneDir}"`,
      "if errorlevel 1 goto move2_failed",
      `echo [%DATE% %TIME%] swap completed >> "${logFile}"`,
      "goto cleanup",
      ":move1_failed",
      `echo [%DATE% %TIME%] ERROR: failed to rename install to .old (errorlevel %errorlevel%) >> "${logFile}"`,
      "goto cleanup",
      ":move2_failed",
      `echo [%DATE% %TIME%] ERROR: failed to promote .new; rolling back >> "${logFile}"`,
      `move /Y "${oldDir}" "${standaloneDir}"`,
      "if errorlevel 1 (",
      `  echo [%DATE% %TIME%] CRITICAL: rollback also failed; manual recovery: move "${oldDir}" "${standaloneDir}" >> "${logFile}"`,
      ") else (",
      `  echo [%DATE% %TIME%] rollback succeeded >> "${logFile}"`,
      ")",
      ":cleanup",
      `del /F /Q "${lockFile}" 2>nul`,
      `del "%~f0"`
    ].join("\r\n");
    const scriptPath = path2.join(
      path2.dirname(standaloneDir),
      "qwen-update.bat"
    );
    fs2.writeFileSync(scriptPath, script);
    spawn("cmd.exe", ["/c", scriptPath], {
      detached: true,
      stdio: "ignore",
      windowsHide: true
    }).unref();
    return "deferred";
  } else {
    fs2.renameSync(standaloneDir, oldDir);
    try {
      fs2.renameSync(newDir, standaloneDir);
    } catch (promoteErr) {
      try {
        fs2.renameSync(oldDir, standaloneDir);
      } catch (rollbackErr) {
        const detail = `Standalone update failed AND rollback failed.
Original error: ${promoteErr.message}
Rollback error: ${rollbackErr.message}
Manual recovery: mv "${oldDir}" "${standaloneDir}"`;
        throw new Error(detail);
      }
      throw promoteErr;
    }
    return "done";
  }
}
__name(atomicReplace, "atomicReplace");
function ensureBinWrapper(standaloneDir, target) {
  const binDir = path2.join(path2.dirname(standaloneDir), "..", "bin");
  try {
    fs2.mkdirSync(binDir, { recursive: true });
    if (target.startsWith("win")) {
      if (UNSAFE_CMD_CHARS.test(standaloneDir)) {
        throw new Error(
          "standaloneDir contains characters unsafe for cmd.exe wrapper"
        );
      }
      const wrapperPath = path2.join(binDir, "qwen.cmd");
      if (!fs2.existsSync(wrapperPath)) {
        const content = `@echo off\r
call "${standaloneDir}\\bin\\qwen.cmd" %*\r
`;
        fs2.writeFileSync(wrapperPath, content);
      }
    } else {
      assertSafeForShellEmbed(standaloneDir, "standaloneDir");
      const wrapperPath = path2.join(binDir, "qwen");
      if (!fs2.existsSync(wrapperPath)) {
        const content = `#!/bin/sh
exec "${standaloneDir}/bin/qwen" "$@"
`;
        fs2.writeFileSync(wrapperPath, content, { mode: 493 });
      }
      ensurePathInShellRc(binDir);
    }
  } catch (err) {
    debugLogger2.warn("Failed to create bin wrapper:", err);
  }
}
__name(ensureBinWrapper, "ensureBinWrapper");
function ensurePathInShellRc(binDir) {
  assertSafeForShellEmbed(binDir, "binDir");
  const shell = process.env["SHELL"] || "";
  let rcFile = null;
  const home = process.env["HOME"] || os.homedir();
  if (shell.endsWith("/zsh")) {
    rcFile = path2.join(home, ".zshrc");
  } else if (shell.endsWith("/bash")) {
    const bashrc = path2.join(home, ".bashrc");
    const profile = path2.join(home, ".bash_profile");
    if (os.platform() === "darwin") {
      rcFile = fs2.existsSync(profile) ? profile : bashrc;
    } else {
      rcFile = bashrc;
    }
  } else if (shell.endsWith("/fish")) {
    rcFile = path2.join(home, ".config", "fish", "config.fish");
  }
  if (!rcFile) return;
  try {
    const content = fs2.existsSync(rcFile) ? fs2.readFileSync(rcFile, "utf-8") : "";
    const marker = "# Added by Qwen Code standalone installer";
    if (content.includes(marker)) return;
    const exportLine = shell.endsWith("/fish") ? `
${marker}
fish_add_path "${binDir}"
` : `
${marker}
export PATH="${binDir}:$PATH"
`;
    fs2.appendFileSync(rcFile, exportLine);
    debugLogger2.info(`Added ${binDir} to ${rcFile}`);
  } catch (err) {
    debugLogger2.debug("Failed to update shell rc:", err);
  }
}
__name(ensurePathInShellRc, "ensurePathInShellRc");
function detectTarget() {
  const platform2 = os.platform();
  const arch2 = os.arch();
  if (platform2 === "darwin") {
    return arch2 === "arm64" ? "darwin-arm64" : "darwin-x64";
  }
  if (platform2 === "win32") return "win-x64";
  if (platform2 === "linux") {
    if (arch2 === "arm64") return "linux-arm64";
    if (arch2 === "x64") return "linux-x64";
  }
  throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
}
__name(detectTarget, "detectTarget");
async function performStandaloneUpdate(standaloneDir, newVersion) {
  const versionPath = normalizeVersion(newVersion);
  let target;
  let isFirstTimeMigration = false;
  const manifestPath = path2.join(standaloneDir, "manifest.json");
  if (fs2.existsSync(manifestPath)) {
    const manifestRaw = fs2.readFileSync(manifestPath, "utf-8");
    const manifest = JSON.parse(manifestRaw);
    target = manifest.target ?? detectTarget();
  } else if (fs2.existsSync(standaloneDir)) {
    throw new Error(
      `${standaloneDir} exists but is not a Qwen Code standalone install. Remove it manually to proceed.`
    );
  } else {
    target = detectTarget();
    isFirstTimeMigration = true;
  }
  validateTarget(target);
  const filename = archiveFilename(target);
  const parentDir = path2.dirname(standaloneDir);
  fs2.mkdirSync(parentDir, { recursive: true });
  const lockPath = path2.join(parentDir, ".qwen-update.lock");
  if (!acquireLock(lockPath)) {
    throw new Error("Another update is already in progress");
  }
  if (isFirstTimeMigration) {
    fs2.mkdirSync(standaloneDir, { recursive: true });
  }
  const tempDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "qwen-code-update-"));
  let extractDir;
  let updateResult;
  try {
    extractDir = fs2.mkdtempSync(path2.join(parentDir, ".qwen-code-update-"));
  } catch (err) {
    fs2.rmSync(tempDir, { recursive: true, force: true });
    releaseLock(lockPath);
    throw err;
  }
  try {
    const archivePath = path2.join(tempDir, filename);
    debugLogger2.info(`Downloading ${filename} (${versionPath})...`);
    const archiveHash = await downloadToFile(
      versionPath,
      filename,
      archivePath
    );
    debugLogger2.info("Verifying checksum...");
    await verifyChecksum(archiveHash, filename, versionPath);
    debugLogger2.info("Extracting archive...");
    await extractArchive(archivePath, extractDir, target);
    const newInstallDir = path2.join(extractDir, "qwen-code");
    if (!fs2.existsSync(path2.join(newInstallDir, "manifest.json"))) {
      throw new Error(
        "Extracted archive does not contain expected qwen-code directory"
      );
    }
    debugLogger2.info("Running smoke test...");
    await smokeTest(newInstallDir, target);
    debugLogger2.info("Replacing installation...");
    updateResult = atomicReplace(standaloneDir, newInstallDir, lockPath);
    const oldDir = `${standaloneDir}.old`;
    if (fs2.existsSync(oldDir)) {
      if (isFirstTimeMigration) {
        fs2.rmSync(oldDir, { recursive: true, force: true });
      } else {
        try {
          const oldManifestPath = path2.join(oldDir, "manifest.json");
          let oldVersion = "unknown";
          if (fs2.existsSync(oldManifestPath)) {
            const oldManifest = JSON.parse(
              fs2.readFileSync(oldManifestPath, "utf-8")
            );
            oldVersion = oldManifest.version || "unknown";
          }
          const rollbackInfo = {
            preservedVersion: oldVersion,
            updatedTo: versionPath,
            timestamp: (/* @__PURE__ */ new Date()).toISOString(),
            reason: "auto-update"
          };
          fs2.writeFileSync(
            path2.join(oldDir, ".qwen-rollback-info.json"),
            JSON.stringify(rollbackInfo, null, 2)
          );
        } catch {
        }
      }
    }
    ensureBinWrapper(standaloneDir, target);
    debugLogger2.info("Standalone update complete.");
    return updateResult;
  } catch (err) {
    const pendingDir = `${standaloneDir}.new`;
    if (fs2.existsSync(pendingDir)) {
      fs2.rmSync(pendingDir, { recursive: true, force: true });
    }
    cleanupEmptyStandaloneDir(standaloneDir);
    throw err;
  } finally {
    if (updateResult !== "deferred") {
      releaseLock(lockPath);
    }
    try {
      fs2.rmSync(tempDir, { recursive: true, force: true });
    } catch {
    }
    try {
      fs2.rmSync(extractDir, { recursive: true, force: true });
    } catch {
    }
  }
}
__name(performStandaloneUpdate, "performStandaloneUpdate");
function rollbackStandaloneUpdate(standaloneDir) {
  const lockPath = path2.join(path2.dirname(standaloneDir), ".qwen-update.lock");
  try {
    const pidStr = fs2.readFileSync(lockPath, "utf-8").trim();
    const pid = parseInt(pidStr, 10);
    if (!Number.isNaN(pid) && isProcessAlive(pid)) {
      return {
        ok: false,
        reason: "rename-failed",
        detail: "An auto-update is currently in progress. Wait for it to finish before rolling back."
      };
    }
  } catch {
  }
  const oldDir = `${standaloneDir}.old`;
  if (!fs2.existsSync(oldDir)) {
    return { ok: false, reason: "no-old", detail: `${oldDir} does not exist` };
  }
  const oldManifest = path2.join(oldDir, "manifest.json");
  if (!fs2.existsSync(oldManifest)) {
    debugLogger2.error("Rollback failed: .old directory has no manifest.json");
    return {
      ok: false,
      reason: "no-manifest",
      detail: `${oldDir}/manifest.json missing \u2014 .old may be corrupt`
    };
  }
  const failedDir = `${standaloneDir}.failed`;
  try {
    if (fs2.existsSync(failedDir)) {
      fs2.rmSync(failedDir, { recursive: true, force: true });
    }
    fs2.renameSync(standaloneDir, failedDir);
    fs2.renameSync(oldDir, standaloneDir);
    debugLogger2.info("Rollback successful.");
    try {
      fs2.rmSync(failedDir, { recursive: true, force: true });
    } catch {
      debugLogger2.debug(`Leftover .failed dir at ${failedDir}, safe to delete`);
    }
    return { ok: true };
  } catch (err) {
    debugLogger2.error("Rollback failed:", err);
    if (!fs2.existsSync(standaloneDir) && fs2.existsSync(failedDir)) {
      try {
        fs2.renameSync(failedDir, standaloneDir);
        return {
          ok: false,
          reason: "rename-failed",
          detail: `Filesystem error: ${err.message}. Current installation was restored automatically.`
        };
      } catch {
      }
    }
    return {
      ok: false,
      reason: "rename-failed",
      detail: `Filesystem error: ${err.message}. Manual recovery: mv "${oldDir}" "${standaloneDir}"`
    };
  }
}
__name(rollbackStandaloneUpdate, "rollbackStandaloneUpdate");

export {
  getInstallationInfo,
  performStandaloneUpdate,
  rollbackStandaloneUpdate
};
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
