#!/bin/bash set -eu umask 077 ORIGIN="https://install.cortex.quantumaifoundry.com" PUBLIC_KEY_DER_BASE64="MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA5GjPMHkgfEU9MnPeIWA1+5AfdBu4luUfU1rRaentkkxLb1jzsFxEB1BkgoBdj6ipQiu8/4JsICOq803Z70Vglva7SIs7va7qrmvGHgJvnINGnLf05K5Himv0uL/x0hge4Tn3wodrARfCaX83umfxFKu3HJ2WBrvX9Klvv6vnLNV4NIK5VLduw9Av9O+oMc/XhFRPKdNRAsMpRbUPti/ZMfa0zkEY0xCyuXtPg72zqWIz/VL9fS3tBLJv2tTGool2nsRAkZbC3jb33NGm0KGTtdG8v/uGA45AEHAop/f3+XqGoHdTq/wKpYexcbnd0mw27sNNUIUznuVAuDwPH7++amQxbI4/TBBEtSZm4niYs8qKDg9l1xdH0TZKFgVw85AJHl55EpZMc1C3h356fpD4+PuBqu1M62asTGcmFk/hzjI+xDMUCvyW643PX1VtbiQtB1/8THrhp9ZXzMJ0hcOp5MO7cd7eYjsV55xuckI2unv92JUCdz21A1+82gMGO6eTAgMBAAE=" KEY_ID="cortex-release-rsa-v1" DATA_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}/quantum-cortex" BIN_ROOT="${XDG_BIN_HOME:-$HOME/.local/bin}" fail() { printf 'Quantum Cortex: %s\n' "$1" >&2 exit 1 } command -v curl >/dev/null 2>&1 || fail "curl_missing" command -v openssl >/dev/null 2>&1 || fail "openssl_missing" command -v python3 >/dev/null 2>&1 || fail "python_3_11_missing" case "$PUBLIC_KEY_DER_BASE64" in __*) fail "release_root_unconfigured" ;; esac python3 -c 'import sys; raise SystemExit(sys.version_info < (3, 11))' \ || fail "python_3_11_missing" OS_NAME=$(uname -s 2>/dev/null || true) ARCH_NAME=$(uname -m 2>/dev/null || true) case "$OS_NAME:$ARCH_NAME" in Darwin:arm64) PLATFORM="darwin-arm64" ;; Darwin:x86_64) PLATFORM="darwin-amd64" ;; *) fail "platform_unsupported" ;; esac WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/cortex-install.XXXXXX") \ || fail "temporary_directory_failed" cleanup() { rm -rf "$WORK_DIR" } trap cleanup EXIT HUP INT TERM MANIFEST="$WORK_DIR/manifest.json" SIGNATURE="$WORK_DIR/manifest.sig" PUBLIC_DER="$WORK_DIR/public.der" PUBLIC_PEM="$WORK_DIR/public.pem" curl --fail --silent --show-error --proto '=https' --tlsv1.2 \ --max-filesize 1048576 \ "$ORIGIN/stable/manifest.json" --output "$MANIFEST" \ || fail "manifest_download_failed" curl --fail --silent --show-error --proto '=https' --tlsv1.2 \ --max-filesize 4096 \ "$ORIGIN/stable/manifest.sig" --output "$SIGNATURE" \ || fail "signature_download_failed" printf '%s' "$PUBLIC_KEY_DER_BASE64" | openssl base64 -d -A -out "$PUBLIC_DER" \ || fail "release_root_invalid" openssl pkey -pubin -inform DER -in "$PUBLIC_DER" -out "$PUBLIC_PEM" >/dev/null 2>&1 \ || fail "release_root_invalid" openssl dgst -sha256 -verify "$PUBLIC_PEM" -signature "$SIGNATURE" "$MANIFEST" >/dev/null 2>&1 \ || fail "manifest_signature_invalid" METADATA="$WORK_DIR/metadata" python3 - "$MANIFEST" "$PLATFORM" "$KEY_ID" > "$METADATA" <<'PY' import json from pathlib import PurePosixPath import re import sys manifest_path, platform, key_id = sys.argv[1:] try: raw = open(manifest_path, "rb").read(1024 * 1024 + 1) if len(raw) > 1024 * 1024: raise ValueError value = json.loads(raw) required = {"format", "sequence", "version", "artifacts", "signing_key_id"} if not isinstance(value, dict) or set(value) != required: raise ValueError if value["format"] != "cortex-client-release/v1" or value["signing_key_id"] != key_id: raise ValueError if not isinstance(value["sequence"], int) or isinstance(value["sequence"], bool) or value["sequence"] < 1: raise ValueError if not isinstance(value["version"], str) or not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.-]+)?", value["version"]): raise ValueError matches = [item for item in value["artifacts"] if isinstance(item, dict) and item.get("platform") == platform] if len(matches) != 1 or set(matches[0]) != {"platform", "path", "bytes", "sha256"}: raise ValueError artifact = matches[0] digest = artifact["sha256"] path = PurePosixPath(artifact["path"]) if not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest): raise ValueError if path.is_absolute() or ".." in path.parts or tuple(path.parts[:2]) != ("artifacts", digest) or len(path.parts) != 3: raise ValueError size = artifact["bytes"] if not isinstance(size, int) or isinstance(size, bool) or not 1 <= size <= 1024 * 1024 * 1024: raise ValueError for result in (str(value["sequence"]), value["version"], artifact["path"], str(size), digest): print(result) except Exception: raise SystemExit(1) PY [ "$?" -eq 0 ] || fail "manifest_invalid" SEQUENCE=$(sed -n '1p' "$METADATA") VERSION=$(sed -n '2p' "$METADATA") ARTIFACT_PATH=$(sed -n '3p' "$METADATA") EXPECTED_BYTES=$(sed -n '4p' "$METADATA") EXPECTED_SHA256=$(sed -n '5p' "$METADATA") ARTIFACT="$WORK_DIR/cortex.pyz" case "$ARTIFACT_PATH" in artifacts/*/cortex.pyz) ;; *) fail "artifact_path_invalid" ;; esac curl --fail --silent --show-error --proto '=https' --tlsv1.2 \ --max-filesize "$EXPECTED_BYTES" \ "$ORIGIN/$ARTIFACT_PATH" --output "$ARTIFACT" \ || fail "artifact_download_failed" ACTUAL_BYTES=$(wc -c < "$ARTIFACT" | tr -d ' ') [ "$ACTUAL_BYTES" = "$EXPECTED_BYTES" ] || fail "artifact_size_invalid" ACTUAL_SHA256=$(openssl dgst -sha256 "$ARTIFACT" | sed 's/^.*= //') [ "$ACTUAL_SHA256" = "$EXPECTED_SHA256" ] || fail "artifact_digest_invalid" MANIFEST_SHA256=$(openssl dgst -sha256 "$MANIFEST" | sed 's/^.*= //') python3 "$ARTIFACT" --help >/dev/null 2>&1 || fail "artifact_self_test_failed" python3 "$ARTIFACT" __install \ --sequence "$SEQUENCE" \ --version "$VERSION" \ --manifest-sha256 "$MANIFEST_SHA256" \ --artifact-sha256 "$EXPECTED_SHA256" \ --data-root "$DATA_ROOT" \ --bin-root "$BIN_ROOT" \ || fail "local_install_failed" printf 'Quantum Cortex %s instalado en %s/cortex\n' "$VERSION" "$BIN_ROOT" case ":$PATH:" in *":$BIN_ROOT:"*) ;; *) printf 'Agrega %s a PATH para usar cortex desde cualquier terminal.\n' "$BIN_ROOT" ;; esac printf 'Instalación completa. En el repositorio que quieras conectar ejecuta:\n' printf ' cortex init --profile PERFIL --instance INSTANCIA --tenant TENANT --project PROYECTO --repository REPOSITORIO --endpoint https://SERVIDOR/mcp --credential CREDENCIAL --cwd "$PWD"\n'