#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \ scripts/run-hypertwist-remote-windows-file-pull.sh \ --map "C:\HyperTwist_worktrees\phase10validate\docs\generated\higher_dimensional_training_maps\phase6c_dedicated_family_package_validation_report.json::docs/generated/higher_dimensional_training_maps/phase6c_dedicated_family_package_validation_report.json" Options: --map :: Copy one remote Windows file back into the local repo. May be repeated. --dry-run Print the pull plan without transferring files. Environment: HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required password for the reverse-SSH Windows user. HYPERTWIST_REMOTE_TUNNEL_PORT Optional, defaults to 22022. HYPERTWIST_REMOTE_WINDOWS_USER Optional, defaults to "anthracite ace". HYPERTWIST_REMOTE_TUNNEL_HOST Optional, defaults to "localhost". EOF } if ! command -v python3 >/dev/null 2>&1; then echo "python3 is required but was not found on PATH." >&2 exit 1 fi if ! command -v sha256sum >/dev/null 2>&1; then echo "sha256sum is required but was not found on PATH." >&2 exit 1 fi repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" remote_ps_wrapper="${repo_root}/scripts/run-hypertwist-remote-windows-powershell.sh" if [[ ! -x "$remote_ps_wrapper" ]]; then echo "Expected executable wrapper at $remote_ps_wrapper" >&2 exit 1 fi dry_run="false" maps=() while [[ $# -gt 0 ]]; do case "$1" in --map) maps+=("${2:-}") shift 2 ;; --dry-run) dry_run="true" shift ;; -h|--help) usage exit 0 ;; *) usage >&2 exit 1 ;; esac done if [[ ${#maps[@]} -eq 0 ]]; then echo "Provide at least one --map." >&2 exit 1 fi ps_single_quote() { printf "'%s'" "${1//\'/\'\'}" } write_base64_to_file() { local destination_path="$1" python3 -c ' import base64 import pathlib import sys destination = pathlib.Path(sys.argv[1]) destination.parent.mkdir(parents=True, exist_ok=True) destination.write_bytes(base64.b64decode(sys.stdin.buffer.read())) ' "$destination_path" } for mapping in "${maps[@]}"; do if [[ "$mapping" != *"::"* ]]; then echo "Map must use ::: $mapping" >&2 exit 1 fi remote_path="${mapping%%::*}" local_path="${mapping#*::}" if [[ -z "$remote_path" || -z "$local_path" ]]; then echo "Map must include both remote and local paths: $mapping" >&2 exit 1 fi if [[ "$local_path" = /* ]]; then local_destination="$local_path" else local_destination="${repo_root}/${local_path}" fi if [[ "$dry_run" == "true" ]]; then printf '%s -> %s\n' "$remote_path" "$local_destination" continue fi remote_payload="$( cat <&2 exit 1 fi base64_payload="$( printf '%s\n' "$remote_output" \ | awk '/^REMOTE_BASE64_BEGIN$/{flag=1; next} /^REMOTE_BASE64_END$/{flag=0} flag' \ | tr -d '\n' )" printf '%s' "$base64_payload" | write_base64_to_file "$local_destination" local_hash="$(sha256sum "$local_destination" | awk '{print tolower($1)}')" if [[ "$local_hash" != "$remote_hash" ]]; then echo "Hash mismatch for $remote_path" >&2 echo " remote: $remote_hash" >&2 echo " local : $local_hash" >&2 exit 1 fi printf 'Pulled %s -> %s (%s)\n' "$remote_path" "$local_destination" "$local_hash" done