Ctrl K
ComunidadGuías…

src/domain/assistant-templates.js

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

Archivo original ↗Preguntar a KiBot
import {
  getAssistantContainerNames,
  getAssistantLabels,
  validateAssistantInstallation,
  validateAssistantSearxngSelection,
} from "./assistant.js";
import { validateDockerName } from "./validation.js";

export const ASSISTANT_COMPANION_IMAGES = Object.freeze({
  api: "ghcr.io/n8n-io/n8n-sandbox-service-api:1.1.1@sha256:21672029fee08495e2398cff7fc370ff60ce0e7c461610732bf2f5265cb75704",
  runner: "ghcr.io/n8n-io/n8n-sandbox-service-runner-dind:1.1.1@sha256:9de7a8aad7f0d2293716daff40206be60577a59a2c2dae641dd9a425c18bf6fd",
  sandbox: "ghcr.io/n8n-io/n8n-sandbox-service-sandbox:1.1.0@sha256:16f62fb90a4ce61ef74925f62ea76bb11eb2a5598888b7c0651100c7944ed2d8",
  searxng: "ghcr.io/searxng/searxng:2026.8.28-a30b2d474@sha256:addd2cf36efb4b9815a2820a522aef7cce4da0d1c0e4527f6675f5663332fc9b",
});

const SANDBOX_SECRET_FIELDS = Object.freeze([
  "sandboxApiKey",
  "runnerRegistrationToken",
  "runnerApiKey",
]);

function createSecret(randomBytes) {
  const value = randomBytes(32);
  if (!Buffer.isBuffer(value) || value.length !== 32) {
    throw new TypeError("The local secret generator is unavailable.");
  }
  return value.toString("base64url");
}

function getRequiredSecretFields(includeSearxng) {
  validateAssistantSearxngSelection(includeSearxng);
  return [...SANDBOX_SECRET_FIELDS, "searxngSecret"];
}

export function createAssistantSecrets({ randomBytes, includeSearxng }) {
  if (typeof randomBytes !== "function") {
    throw new TypeError("A cryptographic random-byte generator is required.");
  }
  const selectedSearxng = validateAssistantSearxngSelection(includeSearxng);
  const secrets = {
    sandboxApiKey: createSecret(randomBytes),
    runnerRegistrationToken: createSecret(randomBytes),
    runnerApiKey: createSecret(randomBytes),
    searxngSecret: createSecret(randomBytes),
  };
  if (new Set(Object.values(secrets)).size !== getRequiredSecretFields(selectedSearxng).length) {
    throw new TypeError("AI Assistant secrets must be independently generated.");
  }
  return secrets;
}

export function createAssistantEnv(secrets, { includeSearxng }) {
  const selectedSearxng = validateAssistantSearxngSelection(includeSearxng);
  const requiredSecretFields = getRequiredSecretFields(selectedSearxng);
  if (
    !secrets ||
    requiredSecretFields.some(
      (field) =>
        typeof secrets[field] !== "string" ||
        !/^[A-Za-z0-9_-]{43}$/u.test(secrets[field]),
    )
  ) {
    throw new TypeError("AI Assistant secrets are invalid.");
  }
  const searxngEnv = `SEARXNG_SECRET=${secrets.searxngSecret}
`;
  return `SANDBOX_API_KEYS=${secrets.sandboxApiKey}
SANDBOX_API_RUNNER_REGISTRATION_TOKEN=${secrets.runnerRegistrationToken}
SANDBOX_API_RUNNER_API_KEY=${secrets.runnerApiKey}
${searxngEnv}`;
}

export function createSearxngSettings() {
  return `use_default_settings: true
search:
  formats:
    - html
    - json
`;
}

export function createAssistantComposeFile({ networkName, installation }) {
  const safeNetworkName = validateDockerName(networkName);
  const safeInstallation = validateAssistantInstallation(installation);
  const containerNames = getAssistantContainerNames(safeInstallation);
  const labels = getAssistantLabels(safeInstallation);

  return `volumes:
  sandbox-tls:
    name: ${safeInstallation.projectName}-sandbox-tls
    labels:
      io.relmio.ai-assistant.managed: "${labels["io.relmio.ai-assistant.managed"]}"
      io.relmio.ai-assistant.install-id: "${labels["io.relmio.ai-assistant.install-id"]}"

services:
  relmio-sandbox-certs:
    container_name: ${containerNames.certs}
    image: ${ASSISTANT_COMPANION_IMAGES.api}
    user: '0:0'
    entrypoint: ['sh', '-c']
    command:
      - >
        bootstrap-mtls.sh --out-dir /tls --api-san relmio-sandbox-api
        --control-san-prefix relmio-sandbox-runner &&
        chown -R sandbox-api:sandbox-api /tls/api
    environment:
      NUM_RUNNERS: '1'
    volumes:
      - sandbox-tls:/tls
    networks:
      - assistant-internal
    labels:
      io.relmio.ai-assistant.managed: "${labels["io.relmio.ai-assistant.managed"]}"
      io.relmio.ai-assistant.install-id: "${labels["io.relmio.ai-assistant.install-id"]}"

  relmio-sandbox-api:
    container_name: ${containerNames.api}
    image: ${ASSISTANT_COMPANION_IMAGES.api}
    depends_on:
      relmio-sandbox-certs:
        condition: service_completed_successfully
    environment:
      SANDBOX_API_KEYS: \${SANDBOX_API_KEYS}
      SANDBOX_API_RUNNER_REGISTRATION_TOKEN: \${SANDBOX_API_RUNNER_REGISTRATION_TOKEN}
      SANDBOX_API_RUNNER_API_KEY: \${SANDBOX_API_RUNNER_API_KEY}
      SANDBOX_API_GRPC_TLS_CERT_FILE: /tls/api/grpc-server.crt
      SANDBOX_API_GRPC_TLS_KEY_FILE: /tls/api/grpc-server.key
      SANDBOX_API_GRPC_TLS_CLIENT_CA_FILE: /tls/api/ca.crt
      SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CA_FILE: /tls/api/ca.crt
      SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CERT_FILE: /tls/api/control-grpc-api-client.crt
      SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_KEY_FILE: /tls/api/control-grpc-api-client.key
      SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_SERVER_NAME: relmio-sandbox-runner-1
    volumes:
      - sandbox-tls:/tls:ro
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s
    networks:
      assistant-internal:
      n8n-shared:
        aliases:
          - ${safeInstallation.sandboxAlias}
    labels:
      io.relmio.ai-assistant.managed: "${labels["io.relmio.ai-assistant.managed"]}"
      io.relmio.ai-assistant.install-id: "${labels["io.relmio.ai-assistant.install-id"]}"

  relmio-sandbox-runner-1:
    container_name: ${containerNames.runner}
    image: ${ASSISTANT_COMPANION_IMAGES.runner}
    privileged: true
    depends_on:
      relmio-sandbox-api:
        condition: service_healthy
    environment:
      SANDBOX_RUNNER_API_KEYS: \${SANDBOX_API_RUNNER_API_KEY}
      SANDBOX_RUNNER_REGISTRATION_TOKEN: \${SANDBOX_API_RUNNER_REGISTRATION_TOKEN}
      SANDBOX_RUNNER_API_GRPC_ADDR: relmio-sandbox-api:9090
      SANDBOX_RUNNER_HTTP_BASE_URL: http://relmio-sandbox-runner-1:8080
      SANDBOX_RUNNER_CONTROL_GRPC_LISTEN_ADDR: ':9091'
      SANDBOX_RUNNER_CONTROL_GRPC_ADVERTISE_ADDR: relmio-sandbox-runner-1:9091
      SANDBOX_RUNNER_ID: runner-1
      SANDBOX_RUNNER_DOCKER_SANDBOX_IMAGE: ${ASSISTANT_COMPANION_IMAGES.sandbox}
      SANDBOX_RUNNER_REGISTRATION_GRPC_CA_FILE: /tls/runner/ca.crt
      SANDBOX_RUNNER_REGISTRATION_GRPC_CERT_FILE: /tls/runner/grpc-client.crt
      SANDBOX_RUNNER_REGISTRATION_GRPC_KEY_FILE: /tls/runner/grpc-client.key
      SANDBOX_RUNNER_REGISTRATION_GRPC_SERVER_NAME: relmio-sandbox-api
      SANDBOX_RUNNER_CONTROL_GRPC_TLS_CERT_FILE: /tls/runner/control-grpc-server.crt
      SANDBOX_RUNNER_CONTROL_GRPC_TLS_KEY_FILE: /tls/runner/control-grpc-server.key
      SANDBOX_RUNNER_CONTROL_GRPC_TLS_CLIENT_CA_FILE: /tls/runner/ca.crt
    volumes:
      - sandbox-tls:/tls:ro
    networks:
      - assistant-internal
    labels:
      io.relmio.ai-assistant.managed: "${labels["io.relmio.ai-assistant.managed"]}"
      io.relmio.ai-assistant.install-id: "${labels["io.relmio.ai-assistant.install-id"]}"

${safeInstallation.includeSearxng ? `  relmio-searxng:
    container_name: ${containerNames.searxng}
    image: ${ASSISTANT_COMPANION_IMAGES.searxng}
    environment:
      SEARXNG_SECRET: \${SEARXNG_SECRET}
    volumes:
      - ./searxng-settings.yml:/etc/searxng/settings.yml:ro
    networks:
      n8n-shared:
        aliases:
          - ${safeInstallation.searxngAlias}
    labels:
      io.relmio.ai-assistant.managed: "${labels["io.relmio.ai-assistant.managed"]}"
      io.relmio.ai-assistant.install-id: "${labels["io.relmio.ai-assistant.install-id"]}"

` : ""}networks:

  assistant-internal:
    name: ${safeInstallation.projectName}-internal
    labels:
      io.relmio.ai-assistant.managed: "${labels["io.relmio.ai-assistant.managed"]}"
      io.relmio.ai-assistant.install-id: "${labels["io.relmio.ai-assistant.install-id"]}"
  n8n-shared:
    external: true
    name: ${safeNetworkName}
`;
}