diff --git a/NEXT_SESSION_PROMPT.md b/NEXT_SESSION_PROMPT.md index aa0766da..b745523a 100644 --- a/NEXT_SESSION_PROMPT.md +++ b/NEXT_SESSION_PROMPT.md @@ -1,10 +1,10 @@ -# Next session: §3's second restructure — libre_translate.sh +# Next session: §3's next restructure — diagnose_pacman_hook_stall.sh > **Paste this whole file into a fresh Claude session opened in `~/testsAndMisc`.** > It is self-contained. Do not go looking for the previous session's context. -Over-cap: **14** (12 shell, 2 kotlin, 1 dart). Working tree clean, `main` in -sync as of commit `071d706`. +Over-cap: **13** (11 shell, 2 kotlin, 1 dart). Working tree clean, `main` in +sync as of the libre_translate.sh archival commit (see §0.5). Two standing decisions from the user, already made — do **not** re-litigate: @@ -34,45 +34,50 @@ be worth a one-line check-in — "do you still use this?" — for anything that looks like it could be dead code, especially in `single_use/` directories. Not a blocking gate, just don't be surprised if the answer changes scope. -## 1. This session's task: `libre_translate.sh` (488 lines, 18 functions) +## 0.5. What just happened (this session) — libre_translate.sh archived, not split -Location: `linux_configuration/scripts/single_use/misc/testsAndMisc-bash/libre_translate.sh` +Asked the same one-line check-in the §0 lesson above recommends, *before* +starting the split (grill-gate questions, before any code was touched this +time). User confirmed `libre_translate.sh` is also unused. Same resolution as +the plagiarism tools: moved to `kuhyx/testsAndMisc-archive` with full git +history via `git filter-repo`, and deleted from this repo. See the evidence +file `docs/superpowers/evidence/archive-libre-translate-2026-08-18.json` for +the exact recipe — this file's history was more tangled than the plagiarism +tools' (two independent pre-reorg root paths, `libre_translate.sh` and +`Bash/libre_translate.sh`, both genuine ancestors via a subtree-import merge) +so both had to be passed to `--path` or history would have been silently +truncated. -**The user already chose the approach** (from the original brief, still -valid): move the whole config-globals cluster into **one lib that owns both -the writes and the reads**. Do not try relocating `parse_args` alone — four -attempts at that failed in an earlier session. Read the file fully before -planning the split; it has real functions already (unlike -`install_plagiarism_tools.sh`, which had almost none), so -`meta/scripts/extract_shell_functions.py` and -`meta/scripts/verify_shell_split.sh` are the right tools here — they were -mostly _inapplicable_ to the plagiarism installer because it lacked function -structure, but this file has 18 functions and should fit their intended use -case. +**Lesson reinforced:** the §0 check-in is now 2 for 2 on `single_use/` files +turning out to be dead code. Keep asking it before every future §3/§4 split, +not just when something "looks like" it could be unused. + +## 1. This session's task: `diagnose_pacman_hook_stall.sh` + +Location: `linux_configuration/scripts/single_use/fixes/diagnose_pacman_hook_stall.sh` +(493 lines, 15 functions). + +**Ask the dead-code check-in first** (see §0.5) — do not assume this one is +live just because the first two candidates weren't. + +If it's confirmed live: `run_one` writes `LAST_ELAPSED`, `main` reads it — +that global crosses the split seam deliberately (see the SC2034 rule in §5 +below), so it must stay in whichever file both functions land in, or get +passed explicitly. Emits **SC2153** (`PACMAN_BIN` vs `PACMAN_PID`) once +split — a real finding to resolve, never to suppress. **Before you start**, confirm scope hasn't drifted: `git status --short` and -`wc -l` on the target file, same as the check that caught the plagiarism -tools were unused this time — it's cheap insurance. - -## 2. Also outstanding in §3 - -`diagnose_pacman_hook_stall.sh` (493 lines, 15 functions) — -`linux_configuration/scripts/single_use/fixes/diagnose_pacman_hook_stall.sh`. -`run_one` writes `LAST_ELAPSED`, `main` reads it — that global crosses the -split seam deliberately (see the SC2034 rule in §5 below), so it must stay in -whichever file both functions land in, or get passed explicitly. Emits -**SC2153** (`PACMAN_BIN` vs `PACMAN_PID`) once split — a real finding to -resolve, never to suppress. Do `libre_translate.sh` first; this one waits its -turn. +`wc -l` on the target file, same as the check that caught both prior files +were unused — it's cheap insurance. ## 3. §4 — do not touch this session `install_leechblock.sh` (485) and `block_compulsive_opening.sh` (705) are copied to `/usr/local/…`, and `pacman_wrapper.sh:831` prefers the deployed copy on **every pacman invocation**. Splitting them naively breaks every -`pacman -S` on this machine. Out of scope until `libre_translate.sh` and -`diagnose_pacman_hook_stall.sh` are both done and the user explicitly says to -continue into §4. +`pacman -S` on this machine. Out of scope until `diagnose_pacman_hook_stall.sh` +is done (or archived — see §0.5) and the user explicitly says to continue +into §4. You cannot run `sudo pacman -S` from the Bash tool — it deadlocks on `db.lck`. Hand the user a `! sudo pacman -S ` line plus the expected @@ -166,10 +171,10 @@ new paths too, or it reports a false `DIFFERENCE`. **`meta/scripts/mutate_shell.py `** — mutation testing against specs in `meta/scripts/fixtures/mutations/`. No spec currently exists for -`libre_translate.sh` or anything outside `phone_focus_mode/`; creating one is -optional, not required by the brief — 100% line coverage with real assertions -is the stated bar, mutation testing is what `phone_focus_mode` additionally -did. +`diagnose_pacman_hook_stall.sh` or anything outside `phone_focus_mode/`; +creating one is optional, not required by the brief — 100% line coverage with +real assertions is the stated bar, mutation testing is what `phone_focus_mode` +additionally did. ## 7. Testing pattern to follow @@ -196,6 +201,7 @@ Last session's plagiarism-tools split established a working pattern for `exit` always terminates the process regardless of `if`/`&&` context. Run it in a subshell: `if (fn args); then ...`. -Check `libre_translate.sh` for anything that shells out (curl? systemctl? -some translate CLI?) before assuming the same fakes apply — it's a different -domain from the plagiarism installer, so the PATH-shim list will differ. +Check `diagnose_pacman_hook_stall.sh` for anything that shells out (pacman? +systemctl? journalctl?) before assuming the same fakes apply — it's a +different domain from the plagiarism installer, so the PATH-shim list will +differ. diff --git a/docs/superpowers/evidence/archive-libre-translate-2026-08-18.json b/docs/superpowers/evidence/archive-libre-translate-2026-08-18.json new file mode 100644 index 00000000..87b7a64d --- /dev/null +++ b/docs/superpowers/evidence/archive-libre-translate-2026-08-18.json @@ -0,0 +1,47 @@ +{ + "intent": "Move the unused libre_translate.sh (LibreTranslate Docker setup script, next §3 restructure target per NEXT_SESSION_PROMPT.md) out of testsAndMisc into testsAndMisc-archive with full git history, per the user's explicit statement that they no longer use it -- decided before any split work began, avoiding the wasted-split outcome from the prior plagiarism-tools session.", + "scope": [ + "linux_configuration/scripts/single_use/misc/testsAndMisc-bash/libre_translate.sh (deleted)", + "kuhyx/testsAndMisc-archive: same file added, with full history including both pre-reorg root paths", + "Not in scope: diagnose_pacman_hook_stall.sh, install_leechblock.sh, block_compulsive_opening.sh, or any other §3/§4 file -- user only confirmed libre_translate.sh is unused" + ], + "changes": [ + "Cloned testsAndMisc into a throwaway scratchpad location (never a worktree of the real repo, since git filter-repo rewrites refs and a worktree shares the object store).", + "Discovered the file has two independent pre-reorg root paths merged via subtree imports: libre_translate.sh (original, commit abba033) and Bash/libre_translate.sh (subtree-imported from the old Bash/ directory, commit 022f5c6, later moved to kuhyx/linux-configuration and re-imported into this repo's linux_configuration/ subtree at a0a2dc1). Both are genuine ancestors of the current file (verified with git merge-base --is-ancestor), unlike a simple single-path rename.", + "Ran git filter-repo --force with explicit --path for all three paths (current path, libre_translate.sh, Bash/libre_translate.sh) so history from both convergent lineages was preserved rather than only the most recent rename chain.", + "Cloned testsAndMisc-archive separately, added the filtered clone as a remote, and merged it in with --allow-unrelated-histories (required since the two repos share no common root commit).", + "Removed the filtered-clone remote and confirmed no stray remote-tracking branches (copilot-wt, etc.) leaked into the local branch list before pushing.", + "Verified the merge landed the file with git ls-tree and pushed to testsAndMisc-archive, then confirmed live via the GitHub contents API and commit-history API before touching the main repo.", + "Only after the archive push was confirmed live: git rm the file from testsAndMisc and committed the deletion." + ], + "verification": [ + { + "command": "gh api repos/kuhyx/testsAndMisc-archive/contents/linux_configuration/scripts/single_use/misc/testsAndMisc-bash", + "result": "pass", + "evidence": "Returned libre_translate.sh, sha 916bf1573edf5e797ac52e93403a47ce0391cb40, 13682 bytes -- confirmed live on GitHub before the main-repo deletion commit was made." + }, + { + "command": "gh api repos/kuhyx/testsAndMisc-archive/commits?path=linux_configuration/scripts/single_use/misc/testsAndMisc-bash/libre_translate.sh", + "result": "pass", + "evidence": "Most recent commit message matches the single_use/periodic_background reorg commit from testsAndMisc, confirming history was carried forward through the rename, not truncated." + }, + { + "command": "git merge-base --is-ancestor 652341c HEAD && git merge-base --is-ancestor c45c17f HEAD (in testsAndMisc, before filtering)", + "result": "pass", + "evidence": "Both pre-reorg commits (root libre_translate.sh and Bash/libre_translate.sh) confirmed as true ancestors, justifying passing both paths to git filter-repo instead of just one." + }, + { + "command": "bash meta/scripts/check_file_length.sh --all", + "result": "pass", + "evidence": "13 files over cap after deletion (was 14 immediately before, including libre_translate.sh at 488 lines) -- the removed file was the only one dropped from the list." + } + ], + "risks": [ + "The archive repo has no pre-commit hooks or CI, so nothing re-verifies the moved script still works there -- it is frozen at exactly the state it left testsAndMisc in.", + "git filter-repo was run with --force on a disposable clone rather than a truly fresh git clone from the remote; verified this clone's origin was the local repo path (harmless) and that no push was made to it." + ], + "rollback": [ + "In testsAndMisc: git revert the deletion commit to restore the file (content and permissions unchanged from before archival, so a straight revert is safe).", + "The testsAndMisc-archive copy can stay regardless of rollback -- having the file in both places briefly is not harmful, just redundant. If it must be undone: git revert the merge commit there, using -m to pick which parent to keep." + ] +} diff --git a/linux_configuration/scripts/single_use/misc/testsAndMisc-bash/libre_translate.sh b/linux_configuration/scripts/single_use/misc/testsAndMisc-bash/libre_translate.sh deleted file mode 100755 index 916bf157..00000000 --- a/linux_configuration/scripts/single_use/misc/testsAndMisc-bash/libre_translate.sh +++ /dev/null @@ -1,488 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# LibreTranslate full setup script (Docker-based) -# Features: -# - Installs Docker if missing (optional --no-docker-install) -# - Pulls libretranslate image (tag configurable) -# - Creates persistent data + cache directories -# - Optionally pre-downloads language models -# - Generates or accepts an API key; can disable auth -# - (Removed) systemd service setup – now always ephemeral -# - Health check + sample translation -# - Uninstall mode removes container, image, service, and data (optional keep data) -# - Idempotent: safe to re-run for upgrades (will pull newer image) - -SCRIPT_NAME=$(basename "$0") -VERSION="1.0.0" - -# Defaults -IMAGE="libretranslate/libretranslate" -TAG="latest" -SERVICE_NAME="libretranslate" -DOCKER_INSTALL=1 -# Systemd removed – always run ephemeral container -API_KEY="" -GENERATE_API_KEY=1 -DISABLE_API_KEY=0 -PORT=5000 -HOST=0.0.0.0 -DATA_DIR="/var/lib/libretranslate" -CACHE_DIR="${DATA_DIR}/cache" -CONFIG_DIR="/etc/libretranslate" -ENV_FILE="${CONFIG_DIR}/libretranslate.env" -PULL_ONLY=0 -PRELOAD_LANGS="" -UNINSTALL=0 -KEEP_DATA=0 -HEALTH_TIMEOUT=15 -EXTRA_ENV=() -NO_COLOR=0 -KEEP_ALIVE=0 -RUN_COMMAND=() -DEBUG=0 - -# Colors -if [[ -t 1 && ${NO_COLOR} -eq 0 ]]; then - GREEN="\e[32m" - YELLOW="\e[33m" - RED="\e[31m" - BLUE="\e[34m" - BOLD="\e[1m" - RESET="\e[0m" -else - GREEN="" - YELLOW="" - RED="" - BLUE="" - BOLD="" - RESET="" -fi - -log() { echo -e "${BLUE}[INFO]${RESET} $*"; } -warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; } -err() { echo -e "${RED}[ERR ]${RESET} $*" >&2; } -success() { echo -e "${GREEN}[OK ]${RESET} $*"; } - -usage() { - cat << EOF -${SCRIPT_NAME} v${VERSION} -Setup or uninstall a self-hosted LibreTranslate instance via Docker. - -Usage: ${SCRIPT_NAME} [options] - -Primary actions: - --uninstall Remove service, container, image (data kept unless --purge or --no-keep-data) - --pull-only Only pull/update image & exit - -Install behavior options: - --image NAME Docker image (default: ${IMAGE}) - --tag TAG Docker tag (default: ${TAG}) - --port N Host port to expose (default: ${PORT}) - --host IP Bind host (default: ${HOST}) - --data-dir PATH Persistent data directory (default: ${DATA_DIR}) - --cache-dir PATH Models cache dir (default: ${CACHE_DIR}) - --no-docker-install Do not attempt to install Docker - (systemd support removed; container is ephemeral) - --keep-alive Keep running (tail logs) until Ctrl-C - -- Treat remaining arguments as a command to run after service is healthy; service stops when command exits - --api-key KEY Use specified API key - --generate-api-key Force generate new random key (default if none provided) - --disable-api-key Disable key requirement (open instance) - --preload-langs CSV Pre-download language models (e.g. en,es,fr,de) - --env K=V Extra environment variable (repeatable) - --health-timeout SEC Wait time for health check (default: ${HEALTH_TIMEOUT}) - --debug Verbose output (do not suppress curl errors; follow logs on failure) - --no-color Disable colored output - -Uninstall options: - --purge Remove data directory (implies --uninstall) - --keep-data Keep data on uninstall (default) - -Misc: - -h, --help Show this help - -v, --version Show version - -Examples: - ${SCRIPT_NAME} --preload-langs en,es,fr --env LT_LOAD_ONLY=en,es,fr - ${SCRIPT_NAME} --api-key mysecret123 --port 8080 - ${SCRIPT_NAME} --uninstall --purge -EOF -} - -gen_api_key() { - # Avoid SIGPIPE issues under set -o pipefail by capturing output first - local key - key=$(head -c 256 /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 40 || true) - if [[ -z $key || ${#key} -lt 40 ]]; then - # Fallback using openssl if available - if command -v openssl > /dev/null 2>&1; then - key=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 40 || true) - fi - fi - if [[ -z $key || ${#key} -lt 20 ]]; then - # Last resort static warning key (should not happen) - key="LT$(date +%s)$$RANDOM" - fi - printf '%s' "$key" -} - -need_cmd() { - command -v "$1" > /dev/null 2>&1 || { - err "Required command '$1' not found" - return 1 - } -} - -parse_args() { - while [[ $# -gt 0 ]]; do - case "$1" in - --image) - IMAGE="$2" - shift 2 - ;; - --tag) - TAG="$2" - shift 2 - ;; - --port) - PORT="$2" - shift 2 - ;; - --host) - HOST="$2" - shift 2 - ;; - --data-dir) - DATA_DIR="$2" - CACHE_DIR="${DATA_DIR}/cache" - shift 2 - ;; - --cache-dir) - CACHE_DIR="$2" - shift 2 - ;; - --no-docker-install) - DOCKER_INSTALL=0 - shift - ;; - --keep-alive) - KEEP_ALIVE=1 - shift - ;; - --) - shift - RUN_COMMAND=("$@") - break - ;; - --api-key) - API_KEY="$2" - GENERATE_API_KEY=0 - shift 2 - ;; - --generate-api-key) - GENERATE_API_KEY=1 - shift - ;; - --disable-api-key) - DISABLE_API_KEY=1 - shift - ;; - --preload-langs) - PRELOAD_LANGS="$2" - shift 2 - ;; - --env) - EXTRA_ENV+=("$2") - shift 2 - ;; - --pull-only) - PULL_ONLY=1 - shift - ;; - --uninstall) - UNINSTALL=1 - shift - ;; - --purge) - UNINSTALL=1 - KEEP_DATA=0 - shift - ;; - --keep-data) - KEEP_DATA=1 - shift - ;; - --health-timeout) - HEALTH_TIMEOUT="$2" - shift 2 - ;; - --no-color) - NO_COLOR=1 - shift - ;; - --debug) - DEBUG=1 - shift - ;; - -h | --help) - usage - exit 0 - ;; - -v | --version) - echo "${VERSION}" - exit 0 - ;; - *) - err "Unknown argument: $1" - usage - exit 1 - ;; - esac - done -} - -ensure_root() { - if [[ $EUID -ne 0 ]]; then - err "This script must run as root (or via sudo)." - exit 1 - fi -} - -install_docker() { - if command -v docker > /dev/null 2>&1; then - log "Docker already installed" - return 0 - fi - if [[ ${DOCKER_INSTALL} -eq 0 ]]; then - err "Docker is not installed and --no-docker-install specified." - exit 1 - fi - log "Installing Docker..." - if command -v apt-get > /dev/null 2>&1; then - apt-get update -y - apt-get install -y ca-certificates curl gnupg - install -d -m 0755 /etc/apt/keyrings - curl -fsSL "https://download.docker.com/linux/$( - . /etc/os-release - echo "$ID" - )/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - echo \ - "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$( - . /etc/os-release - echo "$ID" - ) $( - . /etc/os-release - echo "$VERSION_CODENAME" - ) stable" \ - > /etc/apt/sources.list.d/docker.list - apt-get update -y - apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - else - err "Unsupported package manager. Please install Docker manually." - exit 1 - fi - # Attempt to start docker daemon if dockerd exists and systemctl available; otherwise rely on user - if command -v systemctl > /dev/null 2>&1; then - (systemctl enable --now docker 2> /dev/null && success "Docker installed and started") || warn "Docker installed; ensure dockerd is running" - else - warn "Docker installed; please ensure docker daemon is running" - fi -} - -pull_image() { - log "Pulling image ${IMAGE}:${TAG}" - docker pull "${IMAGE}:${TAG}" - success "Image pulled" -} - -detect_container_user() { - # Determine uid/gid of configured user inside image so host dirs can be chowned - if ! command -v docker > /dev/null 2>&1; then - return 0 - fi - local uid gid - uid=$(docker run --rm --entrypoint /usr/bin/id "${IMAGE}:${TAG}" -u 2> /dev/null || echo "") - gid=$(docker run --rm --entrypoint /usr/bin/id "${IMAGE}:${TAG}" -g 2> /dev/null || echo "") - if [[ -n $uid && -n $gid ]]; then - CONTAINER_UID=$uid - CONTAINER_GID=$gid - fi -} - -write_env_file() { - mkdir -p "${CONFIG_DIR}" "${DATA_DIR}" "${CACHE_DIR}" - detect_container_user - if [[ -n ${CONTAINER_UID:-} && -n ${CONTAINER_GID:-} ]]; then - if command -v stat > /dev/null 2>&1; then - for d in "${DATA_DIR}" "${CACHE_DIR}"; do - if [[ -d $d ]]; then - CUR_UID=$(stat -c %u "$d" 2> /dev/null || echo -1) - if [[ ${CUR_UID} -ne ${CONTAINER_UID} ]]; then - chown "${CONTAINER_UID}":"${CONTAINER_GID}" "$d" 2> /dev/null || warn "Unable to chown $d to ${CONTAINER_UID}:${CONTAINER_GID}" - fi - fi - done - fi - fi - if [[ ${DISABLE_API_KEY} -eq 1 ]]; then - API_KEY_LINE="LT_NO_API_KEY=true" - else - if [[ -z ${API_KEY} && ${GENERATE_API_KEY} -eq 1 ]]; then - API_KEY=$(gen_api_key) - GENERATED=1 - else - GENERATED=0 - fi - API_KEY_LINE="LT_API_KEYS=${API_KEY}" - fi - - { - echo "# LibreTranslate environment file" - echo "# Generated $(date -u +%Y-%m-%dT%H:%M:%SZ)" - echo "${API_KEY_LINE}" - [[ -n ${PRELOAD_LANGS} ]] && echo "LT_PRELOAD_LANGS=${PRELOAD_LANGS}" - for kv in "${EXTRA_ENV[@]:-}"; do echo "$kv"; done - } > "${ENV_FILE}.tmp" - mv "${ENV_FILE}.tmp" "${ENV_FILE}" - chmod 600 "${ENV_FILE}" - success "Environment file written: ${ENV_FILE}" -} - -start_container_ephemeral() { - docker rm -f "${SERVICE_NAME}" > /dev/null 2>&1 || true - docker run -d --name "${SERVICE_NAME}" \ - --env-file "${ENV_FILE}" \ - -v "${DATA_DIR}:/home/libretranslate/.local/share/argos-translate" \ - -v "${CACHE_DIR}:/app/cache" \ - -p "${PORT}:${PORT}" \ - "${IMAGE}:${TAG}" \ - --host 0.0.0.0 --port "${PORT}" - success "Container started (ephemeral)" - echo - echo "Endpoint (pending readiness): http://$(hostname -I | awk '{print $1}'):${PORT}" - echo "Waiting for health..." -} - -health_check() { - local start - start=$(date +%s) - local url="http://127.0.0.1:${PORT}/languages" - local attempt=0 - while true; do - attempt=$((attempt + 1)) - if curl ${DEBUG:+-v} -fsS "$url" > /dev/null 2>&1; then - success "Service healthy (attempt $attempt)" - return 0 - else - [[ $DEBUG -eq 1 ]] && log "Health attempt $attempt failed" - fi - if (($(date +%s) - start > HEALTH_TIMEOUT)); then - err "Health check failed after ${HEALTH_TIMEOUT}s (attempts: $attempt)" - docker logs --tail 200 "${SERVICE_NAME}" || true - return 1 - fi - sleep 0.5 - done -} - -sample_request() { - if [[ ${DISABLE_API_KEY} -eq 0 ]]; then - local key="${API_KEY}" - else - local key="" - fi - log "Performing sample translation (en->es)..." - local DATA='{"q":"Hello world","source":"en","target":"es","format":"text"}' - if [[ -n $key ]]; then - curl -fsS -H "Content-Type: application/json" -H "Authorization: ${key}" -d "$DATA" "http://127.0.0.1:${PORT}/translate" || warn "Sample request failed" - else - curl -fsS -H "Content-Type: application/json" -d "$DATA" "http://127.0.0.1:${PORT}/translate" || warn "Sample request failed" - fi - echo -} - -uninstall_all() { - log "Uninstalling LibreTranslate (ephemeral mode)..." - docker rm -f "${SERVICE_NAME}" 2> /dev/null || true - docker rmi "${IMAGE}:${TAG}" 2> /dev/null || true - if [[ ${KEEP_DATA} -eq 0 ]]; then - rm -rf "${DATA_DIR}" "${CONFIG_DIR}" || true - success "Data directories removed" - else - log "Data kept in ${DATA_DIR} and ${CONFIG_DIR}" - fi - success "Uninstall complete" - exit 0 -} - -main() { - parse_args "$@" - ensure_root - - if [[ ${UNINSTALL} -eq 1 ]]; then - uninstall_all - fi - - install_docker - pull_image - if [[ ${PULL_ONLY} -eq 1 ]]; then - log "Pull-only requested, exiting." - exit 0 - fi - - write_env_file - - # Always ephemeral now - start_container_ephemeral - - health_check - sample_request || true - - # If a command is provided, run it and then shutdown container - if [[ ${#RUN_COMMAND[@]} -gt 0 ]]; then - log "Running user command: ${RUN_COMMAND[*]}" - set +e - "${RUN_COMMAND[@]}" - CMD_STATUS=$? - set -e - log "Command exited with status ${CMD_STATUS}; stopping container" - docker stop "${SERVICE_NAME}" > /dev/null 2>&1 || true - exit ${CMD_STATUS} - fi - - if [[ ${KEEP_ALIVE} -eq 1 ]]; then - log "Tailing logs (Ctrl-C to stop and remove container)" - trap 'log "Stopping container"; docker stop "${SERVICE_NAME}" >/dev/null 2>&1 || true; exit 0' INT TERM - docker logs -f "${SERVICE_NAME}" - log "Logs ended; stopping container" - docker stop "${SERVICE_NAME}" > /dev/null 2>&1 || true - else - log "Ephemeral container left running in background (id: $(docker inspect --format '{{.Id}}' ${SERVICE_NAME} 2> /dev/null || echo unknown))" - log "Stop manually with: docker stop ${SERVICE_NAME}" - fi - - echo - echo "${BOLD}LibreTranslate is ready.${RESET}" - echo "Endpoint: http://$(hostname -I | awk '{print $1}'):${PORT}" - if [[ ${DISABLE_API_KEY} -eq 0 ]]; then - if [[ ${GENERATED:-0} -eq 1 ]]; then - echo "Generated API key: ${API_KEY}" - else - echo "API key: ${API_KEY}" - fi - echo "Use header: Authorization: " - else - echo "API key authentication DISABLED (public instance)." - fi - if [[ -n ${PRELOAD_LANGS} ]]; then - echo "Preloaded languages requested: ${PRELOAD_LANGS}" - fi - echo "Environment file: ${ENV_FILE}" - echo "Manage: docker logs -f ${SERVICE_NAME} | docker stop ${SERVICE_NAME}" - echo "Uninstall: sudo ${SCRIPT_NAME} --uninstall" - echo -} - -main "$@"