Ctrl K
ComunidadGuías…

src/services/claude-login.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { spawn } from "node:child_process";
import { createLocalDockerEnvironment, validateLocalDockerHost } from "../infrastructure/local-process.js";
import { CANDIDATE_VERSION } from "../claude/launcher.js";
import {
  resolveLocalN8nClaudeInstallRoot, acquireLocalN8nClaudeChangeLock,
  attestLocalN8nClaudeInstallation, getLocalN8nClaudeAuthStatus,
} from "./local-n8n-claude-installer.js";

export async function runClaudeCompanionLogin({
  isTTY = process.stdin.isTTY === true && process.stdout.isTTY === true,
  environment = process.env, spawnProcess = spawn,
  resolveInstallRoot = resolveLocalN8nClaudeInstallRoot,
  acquireLock = acquireLocalN8nClaudeChangeLock,
  attestInstallation = attestLocalN8nClaudeInstallation,
  readAuthStatus = getLocalN8nClaudeAuthStatus,
} = {}) {
  if (isTTY !== true) throw new Error("Claude companion sign-in requires an interactive terminal.");
  const installDirectory = await resolveInstallRoot({ env: environment });
  const release = await acquireLock({ installRoot: installDirectory });
  let safeToRelease = true;
  try {
    const attested = await attestInstallation({ installDirectory }, { env: environment });
    if (attested?.installDirectory !== installDirectory || attested.projectName !== "relmio-claude" ||
        attested.serviceName !== "relmio-claude" || !/^[a-f0-9]{64}$/u.test(attested.containerId ?? "")) {
      throw new Error("The managed local Claude companion could not be attested.");
    }
    const dockerHost = validateLocalDockerHost(attested.dockerHost);
    let child;
    try {
      child = spawnProcess("docker", ["--host", dockerHost, "exec", "-it", "-w", "/data/account/empty", "relmio-claude",
        "env", "-i", `TERM=${/^[A-Za-z0-9_.+-]{1,64}$/u.test(environment.TERM ?? "") ? environment.TERM : "xterm-256color"}`,
        "HOME=/home/claude", "USER=claude", "LOGNAME=claude", "CLAUDE_CONFIG_DIR=/data/account/config",
        "PATH=/usr/bin:/bin:/usr/sbin:/sbin", "TMPDIR=/tmp", "/usr/bin/timeout", "--signal=TERM", "--kill-after=2s", "900s",
        `/opt/claude/${CANDIDATE_VERSION}/claude`, "auth", "login"], {
        cwd: installDirectory, env: createLocalDockerEnvironment(environment), shell: false,
        stdio: "inherit", windowsHide: true,
      });
    } catch { throw new Error("Claude companion sign-in could not start."); }
    if (!child || typeof child.once !== "function") throw new Error("Claude companion sign-in could not start.");
    safeToRelease = false;
    const code = await new Promise(resolve => {
      child.once("error", () => resolve(null));
      child.once("close", resolve);
    });
    safeToRelease = Number.isInteger(code);
    if (code !== 0) throw new Error("Claude companion sign-in was not completed.");
    const status = await readAuthStatus({}, { env: environment });
    if (typeof status?.loggedIn !== "boolean" || ![status.authMethod, status.subscriptionType].every(value =>
      value === null || (typeof value === "string" && /^[A-Za-z0-9_.-]{1,64}$/u.test(value)))) {
      throw new Error("Claude companion sign-in status is invalid.");
    }
    return { loggedIn: status.loggedIn, authMethod: status.authMethod, subscriptionType: status.subscriptionType };
  } finally { if (safeToRelease) await release(); }
}