@supacloud/admin 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +156 -12
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -25791,6 +25791,8 @@ var ACTION_POLICY = {
25791
25791
  read: [
25792
25792
  "list",
25793
25793
  "get",
25794
+ "endpoints",
25795
+ "list_endpoints",
25794
25796
  "settings",
25795
25797
  "api_keys",
25796
25798
  "health",
@@ -25900,7 +25902,7 @@ function requestedProjectRef(moduleName, action, args) {
25900
25902
  if (moduleName === "ssh")
25901
25903
  return sshRequestedProjectRef(action, args);
25902
25904
  if (moduleName === "project") {
25903
- return ["list", "create"].includes(action) ? null : stringArgument(args, "ref");
25905
+ return ["list", "list_endpoints", "create"].includes(action) ? null : stringArgument(args, "ref");
25904
25906
  }
25905
25907
  if (moduleName === "platform") {
25906
25908
  return PLATFORM_PROJECT_REF_ACTIONS.has(action) ? stringArgument(args, "ref") : null;
@@ -26376,7 +26378,7 @@ import { Transform as Transform2 } from "node:stream";
26376
26378
  var RELEASE_MANIFEST_NAME = "SUPACLOUD-RELEASE.json";
26377
26379
  var RELEASE_ATTESTATION_NAME = "SUPACLOUD-RELEASE.attestation.jsonl";
26378
26380
  var RELEASE_CHECKSUMS_NAME = "SHA256SUMS";
26379
- var RELEASE_REPOSITORY = "zuohuadong/supacloud";
26381
+ var RELEASE_REPOSITORY = "vibeunion/supacloud";
26380
26382
  var RELEASE_SOURCE_REF = "refs/heads/main";
26381
26383
  var RELEASE_SIGNER_WORKFLOW = `${RELEASE_REPOSITORY}/.github/workflows/release-please.yml`;
26382
26384
  var MEBIBYTE = 1024 * 1024;
@@ -27946,7 +27948,7 @@ async function executeLocalUpgradeTransfer(ssh, request) {
27946
27948
  }
27947
27949
 
27948
27950
  // ../../scripts/lib/release_assets.sh
27949
- var release_assets_default = '#!/usr/bin/env bash\n\nSUPACLOUD_GITHUB_REPOSITORY="${SUPACLOUD_GITHUB_REPOSITORY:-zuohuadong/supacloud}"\nSUPACLOUD_RELEASES_API="${SUPACLOUD_RELEASES_API:-https://api.github.com/repos/${SUPACLOUD_GITHUB_REPOSITORY}/releases}"\nSUPACLOUD_ATTESTATION_SIGNER_WORKFLOW="${SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW:-${SUPACLOUD_GITHUB_REPOSITORY}/.github/workflows/release-please.yml}"\nSUPACLOUD_GH_VERSION="${SUPACLOUD_GH_VERSION:-2.96.0}"\nSUPACLOUD_GH_MIN_VERSION="${SUPACLOUD_GH_MIN_VERSION:-2.68.0}"\nSUPACLOUD_GH_AMD64_SHA256="${SUPACLOUD_GH_AMD64_SHA256:-83d5c2ccad5498f58bf6368acb1ab32588cf43ab3a4b1c301bf36328b1c8bd60}"\nSUPACLOUD_GH_ARM64_SHA256="${SUPACLOUD_GH_ARM64_SHA256:-06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909}"\nSUPACLOUD_RELEASE_ASSETS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"\nSUPACLOUD_ATTESTATION_TRUSTED_ROOT_DEFAULT="${SUPACLOUD_RELEASE_ASSETS_DIR}/../../packages/management-api/src/assets/sigstore-public-good-trusted-root.jsonl"\nreadonly SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SHA256="3c2cc7f357dc064ec527fdcd78da6e9245c21a381e1abaa0f2b62b186bcac1a1"\nreadonly SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SIZE="5748"\n\nsupacloud_curl_release_json() {\n local url="$1"\n local output="$2"\n curl -fsSL --proto \'=https\' --proto-redir \'=https\' \\\n --retry 1 --retry-delay 2 --retry-max-time 60 \\\n --connect-timeout 15 --max-time 30 --speed-limit 128 --speed-time 10 \\\n -o "$output" "$url"\n}\n\nsupacloud_curl_release_asset() {\n local url="$1"\n local output="$2"\n curl -fL --proto \'=https\' --proto-redir \'=https\' \\\n --retry 1 --retry-delay 2 --retry-max-time 180 \\\n --connect-timeout 15 --max-time 90 --speed-limit 128 --speed-time 60 \\\n -o "$output" "$url"\n}\n\nsupacloud_component_tag() {\n local component="$1"\n local version="$2"\n case "$version" in\n "${component}-v"*) printf \'%s\' "$version" ;;\n v*) printf \'%s-%s\' "$component" "$version" ;;\n *) printf \'%s-v%s\' "$component" "$version" ;;\n esac\n}\n\nsupacloud_select_release() {\n local component="$1"\n shift\n local required_assets_json\n [[ $# -gt 0 ]] || {\n echo "at least one required release asset must be specified" >&2\n return 1\n }\n required_assets_json=$(printf \'%s\\n\' "$@" | jq -Rsc \'split("\\n")[:-1]\')\n jq -ce --arg prefix "${component}-v" --argjson required "$required_assets_json" \'\n map(select(\n (.draft | not)\n and (.prerelease | not)\n and (.tag_name | startswith($prefix))\n and (. as $release | all($required[]; . as $asset | any($release.assets[]?; .name == $asset)))\n and any(.assets[]?; .name == "SHA256SUMS")\n ))\n | first\n // error("no matching component release contains all required assets and SHA256SUMS")\n \'\n}\n\nsupacloud_fetch_component_release() {\n local component="$1"\n local version="${2:-latest}"\n shift 2\n local required_assets=("$@")\n local required_assets_json\n local response\n [[ ${#required_assets[@]} -gt 0 ]] || {\n echo "at least one required release asset must be specified" >&2\n return 1\n }\n required_assets_json=$(printf \'%s\\n\' "${required_assets[@]}" | jq -Rsc \'split("\\n")[:-1]\')\n\n if [[ -n "$version" && "$version" != "latest" ]]; then\n local tag\n tag=$(supacloud_component_tag "$component" "$version")\n response=$(supacloud_fetch_release_json "${SUPACLOUD_RELEASES_API}/tags/${tag}") || return 1\n jq -ce --argjson required "$required_assets_json" \'\n select(\n (.draft | not)\n and (.prerelease | not)\n and (. as $release | all($required[]; . as $asset | any($release.assets[]?; .name == $asset)))\n and any(.assets[]?; .name == "SHA256SUMS")\n )\n // error("release does not contain all required assets and SHA256SUMS")\n \' <<< "$response"\n return\n fi\n\n response=$(supacloud_fetch_release_json "${SUPACLOUD_RELEASES_API}?per_page=100") || return 1\n supacloud_select_release "$component" "${required_assets[@]}" <<< "$response"\n}\n\nsupacloud_fetch_release_json() (\n local url="$1"\n local response_file\n response_file=$(mktemp) || return 1\n trap \'rm -f "$response_file"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -f "$response_file"; exit 1\' HUP INT TERM\n supacloud_download_release_metadata_url "$url" "$response_file" || return 1\n cat "$response_file"\n)\n\nsupacloud_release_asset_url() {\n local release_json="$1"\n local asset_name="$2"\n jq -er --arg asset "$asset_name" \'\n first(.assets[]? | select(.name == $asset) | .browser_download_url)\n // error("release asset URL is missing")\n \' <<< "$release_json"\n}\n\nsupacloud_download_url() {\n local url="$1"\n local output="$2"\n local proxy="${SUPACLOUD_GITHUB_PROXY:-${GH_PROXY:-}}"\n\n if supacloud_curl_release_asset "$url" "$output"; then\n return 0\n fi\n if [[ -n "$proxy" ]]; then\n supacloud_curl_release_asset "${proxy%/}/${url}" "$output"\n return\n fi\n return 1\n}\n\nsupacloud_download_release_metadata_url() {\n local url="$1"\n local output="$2"\n local proxy="${SUPACLOUD_GITHUB_PROXY:-${GH_PROXY:-}}"\n\n if supacloud_curl_release_json "$url" "$output"; then\n return 0\n fi\n if [[ -n "$proxy" ]]; then\n supacloud_curl_release_json "${proxy%/}/${url}" "$output"\n return\n fi\n return 1\n}\n\nsupacloud_verify_checksum() {\n local artifact_file="$1"\n local asset_name="$2"\n local checksum_file="$3"\n local expected\n expected=$(awk -v asset="$asset_name" \'$2 == asset || $2 == "*" asset { print $1; exit }\' "$checksum_file")\n if [[ ! "$expected" =~ ^[0-9a-fA-F]{64}$ ]]; then\n echo "SHA256SUMS does not contain a valid checksum for ${asset_name}" >&2\n return 1\n fi\n\n local actual\n actual=$(sha256sum "$artifact_file" | awk \'{print $1}\')\n actual=$(printf \'%s\' "$actual" | tr \'[:upper:]\' \'[:lower:]\')\n expected=$(printf \'%s\' "$expected" | tr \'[:upper:]\' \'[:lower:]\')\n if [[ "$actual" != "$expected" ]]; then\n echo "SHA256 mismatch for ${asset_name}" >&2\n return 1\n fi\n}\n\nsupacloud_validate_binary() {\n local artifact_file="$1"\n local asset_name="$2"\n local description\n description=$(file -b "$artifact_file")\n if [[ "$description" != *ELF* ]]; then\n echo "${asset_name} is not an ELF binary: ${description}" >&2\n return 1\n fi\n\n case "$asset_name" in\n *amd64)\n [[ "$description" == *x86-64* || "$description" == *x86_64* ]] || {\n echo "${asset_name} does not contain an x86-64 ELF binary" >&2\n return 1\n }\n ;;\n *arm64)\n [[ "$description" == *aarch64* || "$description" == *ARM64* ]] || {\n echo "${asset_name} does not contain an arm64 ELF binary" >&2\n return 1\n }\n ;;\n esac\n}\n\nsupacloud_install_pinned_tar_xz_binary() (\n local archive="$1"\n local member="$2"\n local expected_sha256="$3"\n local arch="$4"\n local target="$5"\n local actual_sha256 member_count member_details extract_dir candidate staged_target\n\n actual_sha256=$(sha256sum "$archive" | awk \'{print $1}\')\n actual_sha256=$(printf \'%s\' "$actual_sha256" | tr \'[:upper:]\' \'[:lower:]\')\n expected_sha256=$(printf \'%s\' "$expected_sha256" | tr \'[:upper:]\' \'[:lower:]\')\n if [[ ! "$expected_sha256" =~ ^[0-9a-f]{64}$ || "$actual_sha256" != "$expected_sha256" ]]; then\n echo "SHA256 mismatch for pinned archive" >&2\n return 1\n fi\n\n member_count=$(tar -tJf "$archive" | grep -Fxc "$member" || true)\n if [[ "$member_count" != "1" ]]; then\n echo "Pinned archive must contain the exact member once: $member" >&2\n return 1\n fi\n member_details=$(tar -tvJf "$archive" "$member") || return 1\n if [[ "${member_details:0:1}" != "-" ]]; then\n echo "Pinned archive member is not a regular file: $member" >&2\n return 1\n fi\n\n extract_dir=$(mktemp -d)\n trap \'rm -rf "$extract_dir"; [[ -z "${staged_target:-}" ]] || rm -f "$staged_target"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -rf "$extract_dir"; [[ -z "${staged_target:-}" ]] || rm -f "$staged_target"; exit 1\' HUP INT TERM\n if ! tar --no-same-owner --no-same-permissions -xJf "$archive" -C "$extract_dir" "$member"; then\n return 1\n fi\n candidate="${extract_dir}/${member}"\n supacloud_validate_binary "$candidate" "pinned-linux-${arch}" || return 1\n\n mkdir -p "$(dirname "$target")"\n staged_target=$(mktemp "${target}.tmp.XXXXXX")\n install -m 0755 "$candidate" "$staged_target"\n mv -f "$staged_target" "$target"\n staged_target=""\n)\n\nsupacloud_version_at_least() {\n local current="${1#v}"\n local required="${2#v}"\n local current_major=0 current_minor=0 current_patch=0\n local required_major=0 required_minor=0 required_patch=0\n [[ "$current" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+(-.*)?$ ]] || return 1\n [[ "$required" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+(-.*)?$ ]] || return 1\n IFS=. read -r current_major current_minor current_patch <<EOF\n${current%%-*}\nEOF\n IFS=. read -r required_major required_minor required_patch <<EOF\n${required%%-*}\nEOF\n current_major=${current_major:-0}; current_minor=${current_minor:-0}; current_patch=${current_patch:-0}\n required_major=${required_major:-0}; required_minor=${required_minor:-0}; required_patch=${required_patch:-0}\n if (( current_major != required_major )); then (( current_major > required_major )); return; fi\n if (( current_minor != required_minor )); then (( current_minor > required_minor )); return; fi\n (( current_patch >= required_patch ))\n}\n\nsupacloud_gh_version() {\n gh --version 2>/dev/null | awk \'NR == 1 && $1 == "gh" && $2 == "version" { print $3; exit }\'\n}\n\nsupacloud_install_gh_archive() {\n local archive="$1"\n local version="$2"\n local arch="$3"\n local expected_sha256="$4"\n local target="$5"\n local member="gh_${version}_linux_${arch}/bin/gh"\n local actual_sha256 member_count member_details extracted_dir candidate version_output\n\n actual_sha256=$(sha256sum "$archive" | awk \'{print $1}\')\n if [[ "$actual_sha256" != "$expected_sha256" ]]; then\n echo "GitHub CLI archive SHA256 mismatch" >&2\n return 1\n fi\n\n member_count=$(tar -tzf "$archive" | grep -Fxc "$member" || true)\n if [[ "$member_count" != "1" ]]; then\n echo "GitHub CLI archive does not contain the exact expected member: $member" >&2\n return 1\n fi\n member_details=$(tar -tvzf "$archive" "$member") || return 1\n if [[ "${member_details:0:1}" != "-" ]]; then\n echo "GitHub CLI archive member is not a regular file: $member" >&2\n return 1\n fi\n\n extracted_dir=$(mktemp -d)\n candidate="${extracted_dir}/${member}"\n if ! tar --no-same-owner --no-same-permissions -xzf "$archive" -C "$extracted_dir" "$member" \\\n || ! supacloud_validate_binary "$candidate" "gh-linux-${arch}"; then\n rm -rf "$extracted_dir"\n return 1\n fi\n chmod 0755 "$candidate"\n version_output=$("$candidate" --version 2>/dev/null | head -1) || {\n rm -rf "$extracted_dir"\n echo "GitHub CLI bootstrap binary failed its version check" >&2\n return 1\n }\n if [[ "$version_output" != "gh version ${version}"* ]]; then\n rm -rf "$extracted_dir"\n echo "GitHub CLI bootstrap version mismatch: ${version_output}" >&2\n return 1\n fi\n mkdir -p "$(dirname "$target")"\n install -m 0755 "$candidate" "$target"\n rm -rf "$extracted_dir"\n}\n\nsupacloud_install_pinned_gh() {\n local target="${1:-/usr/local/bin/gh}"\n local machine arch expected_sha256 asset url archive\n machine=$(uname -m)\n case "$machine" in\n x86_64|amd64)\n arch="amd64"\n expected_sha256="$SUPACLOUD_GH_AMD64_SHA256"\n ;;\n aarch64|arm64)\n arch="arm64"\n expected_sha256="$SUPACLOUD_GH_ARM64_SHA256"\n ;;\n *)\n echo "Unsupported architecture for GitHub CLI bootstrap: $machine" >&2\n return 1\n ;;\n esac\n asset="gh_${SUPACLOUD_GH_VERSION}_linux_${arch}.tar.gz"\n url="https://github.com/cli/cli/releases/download/v${SUPACLOUD_GH_VERSION}/${asset}"\n archive=$(mktemp)\n if ! supacloud_download_url "$url" "$archive" \\\n || ! supacloud_install_gh_archive "$archive" "$SUPACLOUD_GH_VERSION" "$arch" "$expected_sha256" "$target"; then\n rm -f "$archive"\n return 1\n fi\n rm -f "$archive"\n}\n\nsupacloud_validate_tar() {\n local artifact_file="$1"\n local entries\n entries=$(tar -tzf "$artifact_file") || {\n echo "Web Console archive is not a readable gzip tarball" >&2\n return 1\n }\n if ! printf \'%s\\n\' "$entries" | awk \'\n /^\\// { exit 1 }\n /(^|\\/)\\.\\.($|\\/)/ { exit 1 }\n \'; then\n echo "Web Console archive contains an unsafe path" >&2\n return 1\n fi\n if ! tar -tvzf "$artifact_file" | awk \'substr($1, 1, 1) != "-" && substr($1, 1, 1) != "d" { exit 1 }\'; then\n echo "Web Console archive contains links or special files" >&2\n return 1\n fi\n printf \'%s\\n\' "$entries" | grep -Eq \'(^|/)index\\.html$\' || {\n echo "Web Console archive is invalid or does not contain index.html" >&2\n return 1\n }\n}\n\nsupacloud_record_integrity_mode() {\n local mode="$1"\n local record_file="${SUPACLOUD_INTEGRITY_MODE_RECORD:-/var/lib/supacloud/artifact-integrity-mode}"\n mkdir -p "$(dirname "$record_file")" 2>/dev/null || return 0\n printf \'%s\\n\' "$mode" > "$record_file" 2>/dev/null || return 0\n chmod 600 "$record_file" 2>/dev/null || true\n}\n\nsupacloud_fetch_attestation_bundle() {\n local artifact_file="$1"\n local bundle_file="$2"\n local digest response\n digest=$(sha256sum "$artifact_file" | awk \'{print $1}\') || return 1\n [[ "$digest" =~ ^[0-9a-fA-F]{64}$ ]] || {\n echo "Unable to calculate the artifact digest for attestation lookup" >&2\n return 1\n }\n digest=$(printf \'%s\' "$digest" | tr \'[:upper:]\' \'[:lower:]\')\n response=$(supacloud_fetch_release_json \\\n "https://api.github.com/repos/${SUPACLOUD_GITHUB_REPOSITORY}/attestations/sha256:${digest}") || {\n echo "Unable to download the public GitHub artifact attestation bundle" >&2\n return 1\n }\n if ! jq -ce \'\n .attestations\n | if type != "array" or length == 0 or any(.[]; (.bundle | type) != "object")\n then error("no valid attestation bundles returned")\n else .[].bundle\n end\n \' <<< "$response" > "$bundle_file"; then\n echo "GitHub artifact attestation response did not contain a valid bundle" >&2\n return 1\n fi\n}\n\nsupacloud_attestation_trusted_root_available() {\n local trusted_root="${SUPACLOUD_ATTESTATION_TRUSTED_ROOT:-$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_DEFAULT}"\n local actual_size actual_sha256\n [[ "$trusted_root" == /* && -f "$trusted_root" && ! -L "$trusted_root" ]] || return 1\n actual_size=$(wc -c < "$trusted_root" | tr -d \'[:space:]\') || return 1\n [[ "$actual_size" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SIZE" ]] || return 1\n actual_sha256=$(sha256sum "$trusted_root" | awk \'{print $1}\') || return 1\n [[ "$actual_sha256" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SHA256" ]] || return 1\n [[ "$(wc -l < "$trusted_root" | tr -d \'[:space:]\')" == "1" ]] || return 1\n jq -e \'type == "object" and .mediaType == "application/vnd.dev.sigstore.trustedroot+json;version=0.1"\' \\\n "$trusted_root" >/dev/null 2>&1\n}\n\nsupacloud_prepare_attestation_trusted_root() {\n local destination="$1"\n local trusted_root="${SUPACLOUD_ATTESTATION_TRUSTED_ROOT:-$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_DEFAULT}"\n supacloud_attestation_trusted_root_available || {\n echo "Pinned Sigstore Public Good trusted root is missing or invalid" >&2\n return 1\n }\n jq -ce . "$trusted_root" > "$destination" || return 1\n chmod 600 "$destination"\n [[ "$(wc -c < "$destination" | tr -d \'[:space:]\')" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SIZE" ]] || return 1\n [[ "$(sha256sum "$destination" | awk \'{print $1}\')" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SHA256" ]]\n}\n\nsupacloud_verify_attestation() (\n local artifact_file="$1"\n if supacloud_attestation_verifier_available; then\n local verification_output bundle_dir bundle_file trusted_root_file\n bundle_dir=$(mktemp -d "${TMPDIR:-/tmp}/supacloud-attestation.XXXXXX") || return 1\n trap \'rm -rf -- "$bundle_dir"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -rf -- "$bundle_dir"; exit 1\' HUP INT TERM\n bundle_file="${bundle_dir}/bundle.jsonl"\n trusted_root_file="${bundle_dir}/trusted_root.jsonl"\n if ! supacloud_fetch_attestation_bundle "$artifact_file" "$bundle_file"; then\n return 1\n fi\n supacloud_prepare_attestation_trusted_root "$trusted_root_file" || return 1\n if ! verification_output=$(gh attestation verify "$artifact_file" \\\n --bundle "$bundle_file" \\\n --custom-trusted-root "$trusted_root_file" \\\n --repo "$SUPACLOUD_GITHUB_REPOSITORY" \\\n --signer-workflow "$SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW" \\\n --source-ref "refs/heads/main" \\\n --deny-self-hosted-runners 2>&1); then\n echo "GitHub artifact attestation verification failed: ${verification_output}" >&2\n return 1\n fi\n supacloud_record_integrity_mode "github-attestation+same-release-sha256"\n return\n fi\n\n if [[ "${SUPACLOUD_ALLOW_UNVERIFIED_RELEASE:-false}" == "true" ]]; then\n echo "BREAK-GLASS LIMITED INTEGRITY MODE: artifact attestation verification is unavailable; only the same-release SHA256 checksum was verified." >&2\n supacloud_record_integrity_mode "break-glass:same-release-sha256-only"\n return 0\n fi\n\n echo "Artifact attestation verification is required, but gh attestation verify is unavailable. Install GitHub CLI or explicitly set SUPACLOUD_ALLOW_UNVERIFIED_RELEASE=true for emergency break-glass use." >&2\n return 1\n)\n\nsupacloud_attestation_verifier_available() {\n local version help\n supacloud_attestation_trusted_root_available || return 1\n command -v gh >/dev/null 2>&1 || return 1\n version=$(supacloud_gh_version)\n [[ -n "$version" ]] || return 1\n supacloud_version_at_least "$version" "$SUPACLOUD_GH_MIN_VERSION" || return 1\n help=$(gh attestation verify --help 2>&1) || return 1\n grep -Eq -- \'(^|[[:space:]])--bundle([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--signer-workflow([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--source-ref([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--custom-trusted-root([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--deny-self-hosted-runners([=[:space:]]|$)\' <<< "$help"\n}\n\nsupacloud_download_release_asset() (\n local release_json="$1"\n local asset_name="$2"\n local destination="$3"\n local asset_kind="$4"\n local asset_url checksum_url temporary_artifact temporary_checksums\n\n asset_url=$(supacloud_release_asset_url "$release_json" "$asset_name") || return 1\n checksum_url=$(supacloud_release_asset_url "$release_json" SHA256SUMS) || return 1\n mkdir -p "$(dirname "$destination")"\n temporary_artifact=$(mktemp "${destination}.tmp.XXXXXX")\n temporary_checksums=$(mktemp "${destination}.SHA256SUMS.tmp.XXXXXX")\n trap \'rm -f "${temporary_artifact:-}" "${temporary_checksums:-}"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -f "${temporary_artifact:-}" "${temporary_checksums:-}"; exit 1\' HUP INT TERM\n\n if ! supacloud_download_url "$asset_url" "$temporary_artifact" \\\n || ! supacloud_download_release_metadata_url "$checksum_url" "$temporary_checksums" \\\n || ! supacloud_verify_checksum "$temporary_artifact" "$asset_name" "$temporary_checksums"; then\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n fi\n\n # Authenticate the digest before parsing archives or inspecting binaries.\n if ! supacloud_verify_attestation "$temporary_artifact"; then\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n fi\n\n case "$asset_kind" in\n binary) supacloud_validate_binary "$temporary_artifact" "$asset_name" ;;\n tar) supacloud_validate_tar "$temporary_artifact" ;;\n *)\n echo "Unknown release asset kind: $asset_kind" >&2\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n ;;\n esac || {\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n }\n\n mv -f "$temporary_artifact" "$destination"\n temporary_artifact=""\n rm -f "$temporary_checksums"\n temporary_checksums=""\n)\n';
27951
+ var release_assets_default = '#!/usr/bin/env bash\n\nSUPACLOUD_GITHUB_REPOSITORY="${SUPACLOUD_GITHUB_REPOSITORY:-vibeunion/supacloud}"\nSUPACLOUD_RELEASES_API="${SUPACLOUD_RELEASES_API:-https://api.github.com/repos/${SUPACLOUD_GITHUB_REPOSITORY}/releases}"\nSUPACLOUD_ATTESTATION_SIGNER_WORKFLOW="${SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW:-${SUPACLOUD_GITHUB_REPOSITORY}/.github/workflows/release-please.yml}"\nSUPACLOUD_GH_VERSION="${SUPACLOUD_GH_VERSION:-2.96.0}"\nSUPACLOUD_GH_MIN_VERSION="${SUPACLOUD_GH_MIN_VERSION:-2.68.0}"\nSUPACLOUD_GH_AMD64_SHA256="${SUPACLOUD_GH_AMD64_SHA256:-83d5c2ccad5498f58bf6368acb1ab32588cf43ab3a4b1c301bf36328b1c8bd60}"\nSUPACLOUD_GH_ARM64_SHA256="${SUPACLOUD_GH_ARM64_SHA256:-06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909}"\nSUPACLOUD_RELEASE_ASSETS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"\nSUPACLOUD_ATTESTATION_TRUSTED_ROOT_DEFAULT="${SUPACLOUD_RELEASE_ASSETS_DIR}/../../packages/management-api/src/assets/sigstore-public-good-trusted-root.jsonl"\nreadonly SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SHA256="3c2cc7f357dc064ec527fdcd78da6e9245c21a381e1abaa0f2b62b186bcac1a1"\nreadonly SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SIZE="5748"\n\nsupacloud_curl_release_json() {\n local url="$1"\n local output="$2"\n curl -fsSL --proto \'=https\' --proto-redir \'=https\' \\\n --retry 1 --retry-delay 2 --retry-max-time 60 \\\n --connect-timeout 15 --max-time 30 --speed-limit 128 --speed-time 10 \\\n -o "$output" "$url"\n}\n\nsupacloud_curl_release_asset() {\n local url="$1"\n local output="$2"\n curl -fL --proto \'=https\' --proto-redir \'=https\' \\\n --retry 1 --retry-delay 2 --retry-max-time 180 \\\n --connect-timeout 15 --max-time 90 --speed-limit 128 --speed-time 60 \\\n -o "$output" "$url"\n}\n\nsupacloud_component_tag() {\n local component="$1"\n local version="$2"\n case "$version" in\n "${component}-v"*) printf \'%s\' "$version" ;;\n v*) printf \'%s-%s\' "$component" "$version" ;;\n *) printf \'%s-v%s\' "$component" "$version" ;;\n esac\n}\n\nsupacloud_select_release() {\n local component="$1"\n shift\n local required_assets_json\n [[ $# -gt 0 ]] || {\n echo "at least one required release asset must be specified" >&2\n return 1\n }\n required_assets_json=$(printf \'%s\\n\' "$@" | jq -Rsc \'split("\\n")[:-1]\')\n jq -ce --arg prefix "${component}-v" --argjson required "$required_assets_json" \'\n map(select(\n (.draft | not)\n and (.prerelease | not)\n and (.tag_name | startswith($prefix))\n and (. as $release | all($required[]; . as $asset | any($release.assets[]?; .name == $asset)))\n and any(.assets[]?; .name == "SHA256SUMS")\n ))\n | first\n // error("no matching component release contains all required assets and SHA256SUMS")\n \'\n}\n\nsupacloud_fetch_component_release() {\n local component="$1"\n local version="${2:-latest}"\n shift 2\n local required_assets=("$@")\n local required_assets_json\n local response\n [[ ${#required_assets[@]} -gt 0 ]] || {\n echo "at least one required release asset must be specified" >&2\n return 1\n }\n required_assets_json=$(printf \'%s\\n\' "${required_assets[@]}" | jq -Rsc \'split("\\n")[:-1]\')\n\n if [[ -n "$version" && "$version" != "latest" ]]; then\n local tag\n tag=$(supacloud_component_tag "$component" "$version")\n response=$(supacloud_fetch_release_json "${SUPACLOUD_RELEASES_API}/tags/${tag}") || return 1\n jq -ce --argjson required "$required_assets_json" \'\n select(\n (.draft | not)\n and (.prerelease | not)\n and (. as $release | all($required[]; . as $asset | any($release.assets[]?; .name == $asset)))\n and any(.assets[]?; .name == "SHA256SUMS")\n )\n // error("release does not contain all required assets and SHA256SUMS")\n \' <<< "$response"\n return\n fi\n\n response=$(supacloud_fetch_release_json "${SUPACLOUD_RELEASES_API}?per_page=100") || return 1\n supacloud_select_release "$component" "${required_assets[@]}" <<< "$response"\n}\n\nsupacloud_fetch_release_json() (\n local url="$1"\n local response_file\n response_file=$(mktemp) || return 1\n trap \'rm -f "$response_file"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -f "$response_file"; exit 1\' HUP INT TERM\n supacloud_download_release_metadata_url "$url" "$response_file" || return 1\n cat "$response_file"\n)\n\nsupacloud_release_asset_url() {\n local release_json="$1"\n local asset_name="$2"\n jq -er --arg asset "$asset_name" \'\n first(.assets[]? | select(.name == $asset) | .browser_download_url)\n // error("release asset URL is missing")\n \' <<< "$release_json"\n}\n\nsupacloud_download_url() {\n local url="$1"\n local output="$2"\n local proxy="${SUPACLOUD_GITHUB_PROXY:-${GH_PROXY:-}}"\n\n if supacloud_curl_release_asset "$url" "$output"; then\n return 0\n fi\n if [[ -n "$proxy" ]]; then\n supacloud_curl_release_asset "${proxy%/}/${url}" "$output"\n return\n fi\n return 1\n}\n\nsupacloud_download_release_metadata_url() {\n local url="$1"\n local output="$2"\n local proxy="${SUPACLOUD_GITHUB_PROXY:-${GH_PROXY:-}}"\n\n if supacloud_curl_release_json "$url" "$output"; then\n return 0\n fi\n if [[ -n "$proxy" ]]; then\n supacloud_curl_release_json "${proxy%/}/${url}" "$output"\n return\n fi\n return 1\n}\n\nsupacloud_verify_checksum() {\n local artifact_file="$1"\n local asset_name="$2"\n local checksum_file="$3"\n local expected\n expected=$(awk -v asset="$asset_name" \'$2 == asset || $2 == "*" asset { print $1; exit }\' "$checksum_file")\n if [[ ! "$expected" =~ ^[0-9a-fA-F]{64}$ ]]; then\n echo "SHA256SUMS does not contain a valid checksum for ${asset_name}" >&2\n return 1\n fi\n\n local actual\n actual=$(sha256sum "$artifact_file" | awk \'{print $1}\')\n actual=$(printf \'%s\' "$actual" | tr \'[:upper:]\' \'[:lower:]\')\n expected=$(printf \'%s\' "$expected" | tr \'[:upper:]\' \'[:lower:]\')\n if [[ "$actual" != "$expected" ]]; then\n echo "SHA256 mismatch for ${asset_name}" >&2\n return 1\n fi\n}\n\nsupacloud_validate_binary() {\n local artifact_file="$1"\n local asset_name="$2"\n local description\n description=$(file -b "$artifact_file")\n if [[ "$description" != *ELF* ]]; then\n echo "${asset_name} is not an ELF binary: ${description}" >&2\n return 1\n fi\n\n case "$asset_name" in\n *amd64)\n [[ "$description" == *x86-64* || "$description" == *x86_64* ]] || {\n echo "${asset_name} does not contain an x86-64 ELF binary" >&2\n return 1\n }\n ;;\n *arm64)\n [[ "$description" == *aarch64* || "$description" == *ARM64* ]] || {\n echo "${asset_name} does not contain an arm64 ELF binary" >&2\n return 1\n }\n ;;\n esac\n}\n\nsupacloud_install_pinned_tar_xz_binary() (\n local archive="$1"\n local member="$2"\n local expected_sha256="$3"\n local arch="$4"\n local target="$5"\n local actual_sha256 member_count member_details extract_dir candidate staged_target\n\n actual_sha256=$(sha256sum "$archive" | awk \'{print $1}\')\n actual_sha256=$(printf \'%s\' "$actual_sha256" | tr \'[:upper:]\' \'[:lower:]\')\n expected_sha256=$(printf \'%s\' "$expected_sha256" | tr \'[:upper:]\' \'[:lower:]\')\n if [[ ! "$expected_sha256" =~ ^[0-9a-f]{64}$ || "$actual_sha256" != "$expected_sha256" ]]; then\n echo "SHA256 mismatch for pinned archive" >&2\n return 1\n fi\n\n member_count=$(tar -tJf "$archive" | grep -Fxc "$member" || true)\n if [[ "$member_count" != "1" ]]; then\n echo "Pinned archive must contain the exact member once: $member" >&2\n return 1\n fi\n member_details=$(tar -tvJf "$archive" "$member") || return 1\n if [[ "${member_details:0:1}" != "-" ]]; then\n echo "Pinned archive member is not a regular file: $member" >&2\n return 1\n fi\n\n extract_dir=$(mktemp -d)\n trap \'rm -rf "$extract_dir"; [[ -z "${staged_target:-}" ]] || rm -f "$staged_target"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -rf "$extract_dir"; [[ -z "${staged_target:-}" ]] || rm -f "$staged_target"; exit 1\' HUP INT TERM\n if ! tar --no-same-owner --no-same-permissions -xJf "$archive" -C "$extract_dir" "$member"; then\n return 1\n fi\n candidate="${extract_dir}/${member}"\n supacloud_validate_binary "$candidate" "pinned-linux-${arch}" || return 1\n\n mkdir -p "$(dirname "$target")"\n staged_target=$(mktemp "${target}.tmp.XXXXXX")\n install -m 0755 "$candidate" "$staged_target"\n mv -f "$staged_target" "$target"\n staged_target=""\n)\n\nsupacloud_version_at_least() {\n local current="${1#v}"\n local required="${2#v}"\n local current_major=0 current_minor=0 current_patch=0\n local required_major=0 required_minor=0 required_patch=0\n [[ "$current" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+(-.*)?$ ]] || return 1\n [[ "$required" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+(-.*)?$ ]] || return 1\n IFS=. read -r current_major current_minor current_patch <<EOF\n${current%%-*}\nEOF\n IFS=. read -r required_major required_minor required_patch <<EOF\n${required%%-*}\nEOF\n current_major=${current_major:-0}; current_minor=${current_minor:-0}; current_patch=${current_patch:-0}\n required_major=${required_major:-0}; required_minor=${required_minor:-0}; required_patch=${required_patch:-0}\n if (( current_major != required_major )); then (( current_major > required_major )); return; fi\n if (( current_minor != required_minor )); then (( current_minor > required_minor )); return; fi\n (( current_patch >= required_patch ))\n}\n\nsupacloud_gh_version() {\n gh --version 2>/dev/null | awk \'NR == 1 && $1 == "gh" && $2 == "version" { print $3; exit }\'\n}\n\nsupacloud_install_gh_archive() {\n local archive="$1"\n local version="$2"\n local arch="$3"\n local expected_sha256="$4"\n local target="$5"\n local member="gh_${version}_linux_${arch}/bin/gh"\n local actual_sha256 member_count member_details extracted_dir candidate version_output\n\n actual_sha256=$(sha256sum "$archive" | awk \'{print $1}\')\n if [[ "$actual_sha256" != "$expected_sha256" ]]; then\n echo "GitHub CLI archive SHA256 mismatch" >&2\n return 1\n fi\n\n member_count=$(tar -tzf "$archive" | grep -Fxc "$member" || true)\n if [[ "$member_count" != "1" ]]; then\n echo "GitHub CLI archive does not contain the exact expected member: $member" >&2\n return 1\n fi\n member_details=$(tar -tvzf "$archive" "$member") || return 1\n if [[ "${member_details:0:1}" != "-" ]]; then\n echo "GitHub CLI archive member is not a regular file: $member" >&2\n return 1\n fi\n\n extracted_dir=$(mktemp -d)\n candidate="${extracted_dir}/${member}"\n if ! tar --no-same-owner --no-same-permissions -xzf "$archive" -C "$extracted_dir" "$member" \\\n || ! supacloud_validate_binary "$candidate" "gh-linux-${arch}"; then\n rm -rf "$extracted_dir"\n return 1\n fi\n chmod 0755 "$candidate"\n version_output=$("$candidate" --version 2>/dev/null | head -1) || {\n rm -rf "$extracted_dir"\n echo "GitHub CLI bootstrap binary failed its version check" >&2\n return 1\n }\n if [[ "$version_output" != "gh version ${version}"* ]]; then\n rm -rf "$extracted_dir"\n echo "GitHub CLI bootstrap version mismatch: ${version_output}" >&2\n return 1\n fi\n mkdir -p "$(dirname "$target")"\n install -m 0755 "$candidate" "$target"\n rm -rf "$extracted_dir"\n}\n\nsupacloud_install_pinned_gh() {\n local target="${1:-/usr/local/bin/gh}"\n local machine arch expected_sha256 asset url archive\n machine=$(uname -m)\n case "$machine" in\n x86_64|amd64)\n arch="amd64"\n expected_sha256="$SUPACLOUD_GH_AMD64_SHA256"\n ;;\n aarch64|arm64)\n arch="arm64"\n expected_sha256="$SUPACLOUD_GH_ARM64_SHA256"\n ;;\n *)\n echo "Unsupported architecture for GitHub CLI bootstrap: $machine" >&2\n return 1\n ;;\n esac\n asset="gh_${SUPACLOUD_GH_VERSION}_linux_${arch}.tar.gz"\n url="https://github.com/cli/cli/releases/download/v${SUPACLOUD_GH_VERSION}/${asset}"\n archive=$(mktemp)\n if ! supacloud_download_url "$url" "$archive" \\\n || ! supacloud_install_gh_archive "$archive" "$SUPACLOUD_GH_VERSION" "$arch" "$expected_sha256" "$target"; then\n rm -f "$archive"\n return 1\n fi\n rm -f "$archive"\n}\n\nsupacloud_validate_tar() {\n local artifact_file="$1"\n local entries\n entries=$(tar -tzf "$artifact_file") || {\n echo "Web Console archive is not a readable gzip tarball" >&2\n return 1\n }\n if ! printf \'%s\\n\' "$entries" | awk \'\n /^\\// { exit 1 }\n /(^|\\/)\\.\\.($|\\/)/ { exit 1 }\n \'; then\n echo "Web Console archive contains an unsafe path" >&2\n return 1\n fi\n if ! tar -tvzf "$artifact_file" | awk \'substr($1, 1, 1) != "-" && substr($1, 1, 1) != "d" { exit 1 }\'; then\n echo "Web Console archive contains links or special files" >&2\n return 1\n fi\n printf \'%s\\n\' "$entries" | grep -Eq \'(^|/)index\\.html$\' || {\n echo "Web Console archive is invalid or does not contain index.html" >&2\n return 1\n }\n}\n\nsupacloud_record_integrity_mode() {\n local mode="$1"\n local record_file="${SUPACLOUD_INTEGRITY_MODE_RECORD:-/var/lib/supacloud/artifact-integrity-mode}"\n mkdir -p "$(dirname "$record_file")" 2>/dev/null || return 0\n printf \'%s\\n\' "$mode" > "$record_file" 2>/dev/null || return 0\n chmod 600 "$record_file" 2>/dev/null || true\n}\n\nsupacloud_fetch_attestation_bundle() {\n local artifact_file="$1"\n local bundle_file="$2"\n local digest response\n digest=$(sha256sum "$artifact_file" | awk \'{print $1}\') || return 1\n [[ "$digest" =~ ^[0-9a-fA-F]{64}$ ]] || {\n echo "Unable to calculate the artifact digest for attestation lookup" >&2\n return 1\n }\n digest=$(printf \'%s\' "$digest" | tr \'[:upper:]\' \'[:lower:]\')\n response=$(supacloud_fetch_release_json \\\n "https://api.github.com/repos/${SUPACLOUD_GITHUB_REPOSITORY}/attestations/sha256:${digest}") || {\n echo "Unable to download the public GitHub artifact attestation bundle" >&2\n return 1\n }\n if ! jq -ce \'\n .attestations\n | if type != "array" or length == 0 or any(.[]; (.bundle | type) != "object")\n then error("no valid attestation bundles returned")\n else .[].bundle\n end\n \' <<< "$response" > "$bundle_file"; then\n echo "GitHub artifact attestation response did not contain a valid bundle" >&2\n return 1\n fi\n}\n\nsupacloud_attestation_trusted_root_available() {\n local trusted_root="${SUPACLOUD_ATTESTATION_TRUSTED_ROOT:-$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_DEFAULT}"\n local actual_size actual_sha256\n [[ "$trusted_root" == /* && -f "$trusted_root" && ! -L "$trusted_root" ]] || return 1\n actual_size=$(wc -c < "$trusted_root" | tr -d \'[:space:]\') || return 1\n [[ "$actual_size" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SIZE" ]] || return 1\n actual_sha256=$(sha256sum "$trusted_root" | awk \'{print $1}\') || return 1\n [[ "$actual_sha256" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SHA256" ]] || return 1\n [[ "$(wc -l < "$trusted_root" | tr -d \'[:space:]\')" == "1" ]] || return 1\n jq -e \'type == "object" and .mediaType == "application/vnd.dev.sigstore.trustedroot+json;version=0.1"\' \\\n "$trusted_root" >/dev/null 2>&1\n}\n\nsupacloud_prepare_attestation_trusted_root() {\n local destination="$1"\n local trusted_root="${SUPACLOUD_ATTESTATION_TRUSTED_ROOT:-$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_DEFAULT}"\n supacloud_attestation_trusted_root_available || {\n echo "Pinned Sigstore Public Good trusted root is missing or invalid" >&2\n return 1\n }\n jq -ce . "$trusted_root" > "$destination" || return 1\n chmod 600 "$destination"\n [[ "$(wc -c < "$destination" | tr -d \'[:space:]\')" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SIZE" ]] || return 1\n [[ "$(sha256sum "$destination" | awk \'{print $1}\')" == "$SUPACLOUD_ATTESTATION_TRUSTED_ROOT_SHA256" ]]\n}\n\nsupacloud_verify_attestation() (\n local artifact_file="$1"\n if supacloud_attestation_verifier_available; then\n local verification_output bundle_dir bundle_file trusted_root_file\n bundle_dir=$(mktemp -d "${TMPDIR:-/tmp}/supacloud-attestation.XXXXXX") || return 1\n trap \'rm -rf -- "$bundle_dir"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -rf -- "$bundle_dir"; exit 1\' HUP INT TERM\n bundle_file="${bundle_dir}/bundle.jsonl"\n trusted_root_file="${bundle_dir}/trusted_root.jsonl"\n if ! supacloud_fetch_attestation_bundle "$artifact_file" "$bundle_file"; then\n return 1\n fi\n supacloud_prepare_attestation_trusted_root "$trusted_root_file" || return 1\n if ! verification_output=$(gh attestation verify "$artifact_file" \\\n --bundle "$bundle_file" \\\n --custom-trusted-root "$trusted_root_file" \\\n --repo "$SUPACLOUD_GITHUB_REPOSITORY" \\\n --signer-workflow "$SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW" \\\n --source-ref "refs/heads/main" \\\n --deny-self-hosted-runners 2>&1); then\n echo "GitHub artifact attestation verification failed: ${verification_output}" >&2\n return 1\n fi\n supacloud_record_integrity_mode "github-attestation+same-release-sha256"\n return\n fi\n\n if [[ "${SUPACLOUD_ALLOW_UNVERIFIED_RELEASE:-false}" == "true" ]]; then\n echo "BREAK-GLASS LIMITED INTEGRITY MODE: artifact attestation verification is unavailable; only the same-release SHA256 checksum was verified." >&2\n supacloud_record_integrity_mode "break-glass:same-release-sha256-only"\n return 0\n fi\n\n echo "Artifact attestation verification is required, but gh attestation verify is unavailable. Install GitHub CLI or explicitly set SUPACLOUD_ALLOW_UNVERIFIED_RELEASE=true for emergency break-glass use." >&2\n return 1\n)\n\nsupacloud_attestation_verifier_available() {\n local version help\n supacloud_attestation_trusted_root_available || return 1\n command -v gh >/dev/null 2>&1 || return 1\n version=$(supacloud_gh_version)\n [[ -n "$version" ]] || return 1\n supacloud_version_at_least "$version" "$SUPACLOUD_GH_MIN_VERSION" || return 1\n help=$(gh attestation verify --help 2>&1) || return 1\n grep -Eq -- \'(^|[[:space:]])--bundle([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--signer-workflow([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--source-ref([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--custom-trusted-root([=[:space:]]|$)\' <<< "$help" || return 1\n grep -Eq -- \'(^|[[:space:]])--deny-self-hosted-runners([=[:space:]]|$)\' <<< "$help"\n}\n\nsupacloud_download_release_asset() (\n local release_json="$1"\n local asset_name="$2"\n local destination="$3"\n local asset_kind="$4"\n local asset_url checksum_url temporary_artifact temporary_checksums\n\n asset_url=$(supacloud_release_asset_url "$release_json" "$asset_name") || return 1\n checksum_url=$(supacloud_release_asset_url "$release_json" SHA256SUMS) || return 1\n mkdir -p "$(dirname "$destination")"\n temporary_artifact=$(mktemp "${destination}.tmp.XXXXXX")\n temporary_checksums=$(mktemp "${destination}.SHA256SUMS.tmp.XXXXXX")\n trap \'rm -f "${temporary_artifact:-}" "${temporary_checksums:-}"\' EXIT\n trap \'trap - EXIT HUP INT TERM; rm -f "${temporary_artifact:-}" "${temporary_checksums:-}"; exit 1\' HUP INT TERM\n\n if ! supacloud_download_url "$asset_url" "$temporary_artifact" \\\n || ! supacloud_download_release_metadata_url "$checksum_url" "$temporary_checksums" \\\n || ! supacloud_verify_checksum "$temporary_artifact" "$asset_name" "$temporary_checksums"; then\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n fi\n\n # Authenticate the digest before parsing archives or inspecting binaries.\n if ! supacloud_verify_attestation "$temporary_artifact"; then\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n fi\n\n case "$asset_kind" in\n binary) supacloud_validate_binary "$temporary_artifact" "$asset_name" ;;\n tar) supacloud_validate_tar "$temporary_artifact" ;;\n *)\n echo "Unknown release asset kind: $asset_kind" >&2\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n ;;\n esac || {\n rm -f "$temporary_artifact" "$temporary_checksums"\n return 1\n }\n\n mv -f "$temporary_artifact" "$destination"\n temporary_artifact=""\n rm -f "$temporary_checksums"\n temporary_checksums=""\n)\n';
27950
27952
 
27951
27953
  // src/shared/tools/ssh-tools.ts
27952
27954
  var SAFE_CONTAINER_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/;
@@ -28907,7 +28909,7 @@ Actions: ping, setup, install, upgrade, versions, diagnose, exec, troubleshoot,
28907
28909
  const CONFIG = "/etc/supabase/install.env";
28908
28910
  const INPUT = `/etc/supabase/.install-input-${installId}.env`;
28909
28911
  const BOOTSTRAP = `/opt/.supacloud-bootstrap-${installId}`;
28910
- const REPO = "https://github.com/zuohuadong/supacloud.git";
28912
+ const REPO = "https://github.com/vibeunion/supacloud.git";
28911
28913
  const configuredProxy = args.github_proxy ? assertSafeGithubProxy(args.github_proxy) : "direct";
28912
28914
  const proxyDisabled = ["direct", "none"].includes(configuredProxy.toLowerCase());
28913
28915
  const proxyPrefix = proxyDisabled ? "" : configuredProxy.endsWith("/") ? configuredProxy : `${configuredProxy}/`;
@@ -30427,6 +30429,133 @@ function projectGetRead(response, expectedRef) {
30427
30429
  const project = projectDetails(response.data, expectedRef);
30428
30430
  return project ? successfulResult(project) : failedResult("Invalid project response");
30429
30431
  }
30432
+ // ../cli/src/shared/tools/project-endpoint-read.ts
30433
+ var PROJECT_ENDPOINT_RESPONSE_MAX_BYTES = 256 * 1024;
30434
+ var PROJECT_ENDPOINT_LIST_RESPONSE_MAX_BYTES = 1024 * 1024;
30435
+ var PROJECT_REF_PATTERN2 = /^[a-z0-9-]{1,20}$/;
30436
+ var PROJECT_ENDPOINTS_SCHEMA = "supacloud.project-endpoints.v1";
30437
+ var PROJECT_ENDPOINT_SOURCES = new Set([
30438
+ "explicit_api_domain",
30439
+ "explicit_auth_domain",
30440
+ "explicit_studio_domain",
30441
+ "custom_domain",
30442
+ "derived_api_domain",
30443
+ "generated"
30444
+ ]);
30445
+ var ROOT_KEYS = new Set(["schema", "project_ref", "endpoints"]);
30446
+ var ENDPOINTS_KEYS = new Set(["api", "auth", "studio"]);
30447
+ var ENDPOINT_KEYS = new Set(["origin", "host", "scheme", "source", "aliases"]);
30448
+ var MAX_PROJECTS2 = 1e4;
30449
+ var MAX_ALIASES = 64;
30450
+ function plainRecord2(candidate) {
30451
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
30452
+ return null;
30453
+ const prototype = Object.getPrototypeOf(candidate);
30454
+ return prototype === Object.prototype || prototype === null ? candidate : null;
30455
+ }
30456
+ function hasOnlyKeys2(record, allowedKeys) {
30457
+ return Object.keys(record).every((key) => allowedKeys.has(key));
30458
+ }
30459
+ function boundedText2(candidate, maxLength) {
30460
+ return typeof candidate === "string" && candidate.length > 0 && candidate.length <= maxLength && !/[\u0000-\u001f\u007f]/u.test(candidate) ? candidate : null;
30461
+ }
30462
+ function canonicalHost(candidate, scheme) {
30463
+ const host = boundedText2(candidate, 255);
30464
+ if (!host)
30465
+ return null;
30466
+ try {
30467
+ const parsed = new URL(`${scheme}://${host}`);
30468
+ return parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.host !== host ? null : host;
30469
+ } catch {
30470
+ return null;
30471
+ }
30472
+ }
30473
+ function projectEndpoint2(candidate) {
30474
+ const endpoint = plainRecord2(candidate);
30475
+ if (!endpoint || !hasOnlyKeys2(endpoint, ENDPOINT_KEYS))
30476
+ return null;
30477
+ const scheme = endpoint.scheme === "http" || endpoint.scheme === "https" ? endpoint.scheme : null;
30478
+ const origin = boundedText2(endpoint.origin, 2048);
30479
+ const source = boundedText2(endpoint.source, 64);
30480
+ if (!scheme || !origin || !source || !PROJECT_ENDPOINT_SOURCES.has(source))
30481
+ return null;
30482
+ let parsedOrigin;
30483
+ try {
30484
+ parsedOrigin = new URL(origin);
30485
+ } catch {
30486
+ return null;
30487
+ }
30488
+ if (parsedOrigin.protocol !== `${scheme}:` || parsedOrigin.origin !== origin || parsedOrigin.username || parsedOrigin.password || parsedOrigin.pathname !== "/" || parsedOrigin.search || parsedOrigin.hash)
30489
+ return null;
30490
+ const host = canonicalHost(endpoint.host, scheme);
30491
+ if (!host || host !== parsedOrigin.host || !Array.isArray(endpoint.aliases) || endpoint.aliases.length > MAX_ALIASES)
30492
+ return null;
30493
+ const aliases = [];
30494
+ const seenAliases = new Set;
30495
+ for (const aliasCandidate of endpoint.aliases) {
30496
+ const alias = canonicalHost(aliasCandidate, scheme);
30497
+ if (!alias || alias === host || seenAliases.has(alias))
30498
+ return null;
30499
+ seenAliases.add(alias);
30500
+ aliases.push(alias);
30501
+ }
30502
+ return { origin, host, scheme, source, aliases };
30503
+ }
30504
+ function projectEndpointProjection(candidate) {
30505
+ const projection = plainRecord2(candidate);
30506
+ if (!projection || !hasOnlyKeys2(projection, ROOT_KEYS) || projection.schema !== PROJECT_ENDPOINTS_SCHEMA || typeof projection.project_ref !== "string" || !PROJECT_REF_PATTERN2.test(projection.project_ref))
30507
+ return null;
30508
+ const endpoints = plainRecord2(projection.endpoints);
30509
+ if (!endpoints || !hasOnlyKeys2(endpoints, ENDPOINTS_KEYS))
30510
+ return null;
30511
+ const api = projectEndpoint2(endpoints.api);
30512
+ const auth = projectEndpoint2(endpoints.auth);
30513
+ const studio = projectEndpoint2(endpoints.studio);
30514
+ return api && auth && studio ? {
30515
+ schema: PROJECT_ENDPOINTS_SCHEMA,
30516
+ project_ref: projection.project_ref,
30517
+ endpoints: { api, auth, studio }
30518
+ } : null;
30519
+ }
30520
+ function validHttpStatus2(status) {
30521
+ return Number.isSafeInteger(status) && status >= 100 && status <= 599;
30522
+ }
30523
+ function successfulResponse2(response) {
30524
+ return response.ok === true && validHttpStatus2(response.status) && response.status >= 200 && response.status <= 299;
30525
+ }
30526
+ function failedResult2(message) {
30527
+ return { text: `❌ ${message}`, isError: true };
30528
+ }
30529
+ function failedHttpResult2(label, status) {
30530
+ return failedResult2(validHttpStatus2(status) ? `${label} request failed (${status})` : `${label} request failed`);
30531
+ }
30532
+ function successfulResult2(payload) {
30533
+ return { text: JSON.stringify(payload, null, 2), isError: false };
30534
+ }
30535
+ function projectEndpointRead(response, expectedRef) {
30536
+ if (!successfulResponse2(response))
30537
+ return failedHttpResult2("Project endpoints", response.status);
30538
+ const projection = projectEndpointProjection(response.data);
30539
+ return projection && projection.project_ref === expectedRef ? successfulResult2(projection) : failedResult2("Invalid project endpoint response");
30540
+ }
30541
+ function projectEndpointListRead(response) {
30542
+ if (!successfulResponse2(response))
30543
+ return failedHttpResult2("Project endpoint list", response.status);
30544
+ if (!Array.isArray(response.data) || response.data.length > MAX_PROJECTS2) {
30545
+ return failedResult2("Invalid project endpoint list response");
30546
+ }
30547
+ const projections = [];
30548
+ const refs = new Set;
30549
+ for (const candidate of response.data) {
30550
+ const projection = projectEndpointProjection(candidate);
30551
+ if (!projection || refs.has(projection.project_ref)) {
30552
+ return failedResult2("Invalid project endpoint list response");
30553
+ }
30554
+ refs.add(projection.project_ref);
30555
+ projections.push(projection);
30556
+ }
30557
+ return successfulResult2(projections);
30558
+ }
30430
30559
  // src/shared/tools/project-runtime-snapshot.ts
30431
30560
  var RUNTIME_SNAPSHOT_SCHEMA = "supacloud.runtime-snapshot.v1";
30432
30561
  var ATTESTED_REVISION_PATTERN = /^hmac-sha256:[a-f0-9]{64}$/;
@@ -30914,13 +31043,18 @@ function resolveRef(refFromArgs, defaultRef) {
30914
31043
  throw new Error("'ref' is required for this action");
30915
31044
  return ref;
30916
31045
  }
31046
+ function projectEndpointProjectionPath(ref) {
31047
+ return `/v1/projects/${encodeURIComponent(ref)}/endpoint/projection`;
31048
+ }
30917
31049
  function registerAdminProjectCliTools(server, http, options = {}) {
30918
31050
  const fileOperations = options.projectEnvFileOperations;
30919
- server.tool("project", "Platform-level project lifecycle management. Actions: list, create, get, delete, pause, restore, restart, settings, update_settings, api_keys, health, logs, tasks, services, runtime_snapshot, service_control", {
31051
+ server.tool("project", "Platform-level project lifecycle management. Actions: list, list_endpoints, create, get, endpoints, delete, pause, restore, restart, settings, update_settings, api_keys, health, logs, tasks, services, runtime_snapshot, service_control", {
30920
31052
  action: withDescription(stringEnum([
30921
31053
  "list",
31054
+ "list_endpoints",
30922
31055
  "create",
30923
31056
  "get",
31057
+ "endpoints",
30924
31058
  "delete",
30925
31059
  "pause",
30926
31060
  "restore",
@@ -30935,7 +31069,7 @@ function registerAdminProjectCliTools(server, http, options = {}) {
30935
31069
  "runtime_snapshot",
30936
31070
  "service_control"
30937
31071
  ]), "Action to perform"),
30938
- ref: optional(Type.String(), "[*] Project ref (required for most actions except 'list' and 'create')"),
31072
+ ref: optional(Type.String(), "[*] Project ref (required for most actions except 'list', 'list_endpoints', and 'create')"),
30939
31073
  name: optional(Type.String(), "[create] Project name"),
30940
31074
  region: optional(Type.String(), "[create] Region (default: local)"),
30941
31075
  organization_id: optional(Type.String(), "[create] Organization ID"),
@@ -30972,6 +31106,10 @@ function registerAdminProjectCliTools(server, http, options = {}) {
30972
31106
  return projectReadResponse(projectListRead(await http.get("/v1/projects", {
30973
31107
  maxResponseBytes: PROJECT_READ_RESPONSE_MAX_BYTES
30974
31108
  })));
31109
+ case "list_endpoints":
31110
+ return projectReadResponse(projectEndpointListRead(await http.get("/v1/projects/endpoints", {
31111
+ maxResponseBytes: PROJECT_ENDPOINT_LIST_RESPONSE_MAX_BYTES
31112
+ })));
30975
31113
  case "create": {
30976
31114
  if (!name)
30977
31115
  throw new Error("'name' is required for create");
@@ -31015,6 +31153,12 @@ function registerAdminProjectCliTools(server, http, options = {}) {
31015
31153
  maxResponseBytes: PROJECT_READ_RESPONSE_MAX_BYTES
31016
31154
  }), resolvedRef));
31017
31155
  }
31156
+ case "endpoints": {
31157
+ const resolvedRef = resolveRef(ref);
31158
+ return projectReadResponse(projectEndpointRead(await http.get(projectEndpointProjectionPath(resolvedRef), {
31159
+ maxResponseBytes: PROJECT_ENDPOINT_RESPONSE_MAX_BYTES
31160
+ }), resolvedRef));
31161
+ }
31018
31162
  case "delete": {
31019
31163
  const resolvedRef = resolveRef(ref);
31020
31164
  text = simple(await http.delete(`/v1/projects/${resolvedRef}`), `Project ${resolvedRef} deleted`);
@@ -31425,7 +31569,7 @@ import { createHash as createHash3 } from "node:crypto";
31425
31569
  import { constants as fsConstants2 } from "node:fs";
31426
31570
  import { open as open2 } from "node:fs/promises";
31427
31571
  import { resolve as resolve3 } from "node:path";
31428
- var PROJECT_REF_PATTERN2 = /^[A-Za-z0-9_-]{1,20}$/u;
31572
+ var PROJECT_REF_PATTERN3 = /^[A-Za-z0-9_-]{1,20}$/u;
31429
31573
  var DEPLOYMENT_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/u;
31430
31574
  var RELEASE_ID_PATTERN = /^[0-9a-f]{64}$/u;
31431
31575
  var MUTATION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
@@ -31468,7 +31612,7 @@ function releaseRecord(candidate) {
31468
31612
  "created_at",
31469
31613
  "kind"
31470
31614
  ];
31471
- if (!exactKeys(record, keys) || record.schema !== "supacloud.frontend-release.v1" || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN2.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || typeof record.release_id !== "string" || !RELEASE_ID_PATTERN.test(record.release_id) || record.sha256 !== record.release_id || typeof record.tree_sha256 !== "string" || !RELEASE_ID_PATTERN.test(record.tree_sha256) || !Number.isSafeInteger(record.size_bytes) || Number(record.size_bytes) < 1 || !Number.isSafeInteger(record.file_count) || Number(record.file_count) < 1 || !canonicalTimestamp3(record.created_at) || record.kind !== "prebuilt_static")
31615
+ if (!exactKeys(record, keys) || record.schema !== "supacloud.frontend-release.v1" || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN3.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || typeof record.release_id !== "string" || !RELEASE_ID_PATTERN.test(record.release_id) || record.sha256 !== record.release_id || typeof record.tree_sha256 !== "string" || !RELEASE_ID_PATTERN.test(record.tree_sha256) || !Number.isSafeInteger(record.size_bytes) || Number(record.size_bytes) < 1 || !Number.isSafeInteger(record.file_count) || Number(record.file_count) < 1 || !canonicalTimestamp3(record.created_at) || record.kind !== "prebuilt_static")
31472
31616
  return null;
31473
31617
  return {
31474
31618
  project_ref: record.project_ref,
@@ -31514,7 +31658,7 @@ function releaseInventory(candidate, expected) {
31514
31658
  "releases",
31515
31659
  "next_cursor"
31516
31660
  ];
31517
- if (!exactKeys(record, keys) || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN2.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || !Array.isArray(record.releases))
31661
+ if (!exactKeys(record, keys) || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN3.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || !Array.isArray(record.releases))
31518
31662
  return null;
31519
31663
  const activeReleaseId = nullableIdentity(record.active_release_id, RELEASE_ID_PATTERN);
31520
31664
  const activeActivationId = nullableIdentity(record.active_activation_id, MUTATION_ID_PATTERN);
@@ -31535,7 +31679,7 @@ function releaseInventory(candidate, expected) {
31535
31679
  };
31536
31680
  }
31537
31681
  function releaseEndpoint(projectRef, deploymentId) {
31538
- if (!PROJECT_REF_PATTERN2.test(projectRef))
31682
+ if (!PROJECT_REF_PATTERN3.test(projectRef))
31539
31683
  throw new Error("'ref' is invalid for frontend releases");
31540
31684
  if (!DEPLOYMENT_ID_PATTERN.test(deploymentId))
31541
31685
  throw new Error("'id' is invalid for frontend releases");
@@ -31896,7 +32040,7 @@ Actions: list_releases, get_release, upload_release, activate_release`, {
31896
32040
  // package.json
31897
32041
  var package_default = {
31898
32042
  name: "@supacloud/admin",
31899
- version: "0.14.2",
32043
+ version: "0.15.0",
31900
32044
  description: "Platform administration CLI for SupaCloud operators",
31901
32045
  type: "module",
31902
32046
  main: "./dist/index.js",
@@ -31922,7 +32066,7 @@ var package_default = {
31922
32066
  license: "MIT",
31923
32067
  repository: {
31924
32068
  type: "git",
31925
- url: "https://github.com/zuohuadong/supacloud.git",
32069
+ url: "https://github.com/vibeunion/supacloud.git",
31926
32070
  directory: "packages/admin"
31927
32071
  },
31928
32072
  dependencies: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/admin",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "description": "Platform administration CLI for SupaCloud operators",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "license": "MIT",
27
27
  "repository": {
28
28
  "type": "git",
29
- "url": "https://github.com/zuohuadong/supacloud.git",
29
+ "url": "https://github.com/vibeunion/supacloud.git",
30
30
  "directory": "packages/admin"
31
31
  },
32
32
  "dependencies": {