Spaces:
Running
Running
Merge pull request #10 from anurag162008/revert-9-codex/find-automatic-package-installation-method
Browse filesRevert "Enable ephemeral package installs: restricted sudo + startup replay for HF Spaces"
- Dockerfile +2 -11
- README.md +0 -44
- start.sh +12 -358
Dockerfile
CHANGED
|
@@ -14,7 +14,6 @@ ARG OPENCLAW_VERSION=latest
|
|
| 14 |
# Install system dependencies
|
| 15 |
RUN apt-get update && apt-get install -y \
|
| 16 |
git \
|
| 17 |
-
sudo \
|
| 18 |
ca-certificates \
|
| 19 |
jq \
|
| 20 |
curl \
|
|
@@ -45,17 +44,9 @@ RUN apt-get update && apt-get install -y \
|
|
| 45 |
pip3 install --no-cache-dir --break-system-packages huggingface_hub && \
|
| 46 |
rm -rf /var/lib/apt/lists/*
|
| 47 |
|
| 48 |
-
# Reuse existing node user (UID 1000)
|
| 49 |
-
# commands only so runtime apt installs can be replayed after HF Space restarts
|
| 50 |
-
# without granting unrestricted sudo access.
|
| 51 |
RUN mkdir -p /home/node/app /home/node/.openclaw && \
|
| 52 |
-
chown -R 1000:1000 /home/node
|
| 53 |
-
printf '%s\n' \
|
| 54 |
-
'Cmnd_Alias HUGGINGCLAW_APT = /usr/bin/apt, /usr/bin/apt-get, /usr/bin/dpkg' \
|
| 55 |
-
'node ALL=(root) NOPASSWD: HUGGINGCLAW_APT' \
|
| 56 |
-
> /etc/sudoers.d/huggingclaw-apt && \
|
| 57 |
-
chmod 0440 /etc/sudoers.d/huggingclaw-apt && \
|
| 58 |
-
visudo -cf /etc/sudoers.d/huggingclaw-apt
|
| 59 |
|
| 60 |
# Copy pre-built OpenClaw (skips npm install entirely — much faster!)
|
| 61 |
COPY --from=openclaw --chown=1000:1000 /app /home/node/.openclaw/openclaw-app
|
|
|
|
| 14 |
# Install system dependencies
|
| 15 |
RUN apt-get update && apt-get install -y \
|
| 16 |
git \
|
|
|
|
| 17 |
ca-certificates \
|
| 18 |
jq \
|
| 19 |
curl \
|
|
|
|
| 44 |
pip3 install --no-cache-dir --break-system-packages huggingface_hub && \
|
| 45 |
rm -rf /var/lib/apt/lists/*
|
| 46 |
|
| 47 |
+
# Reuse existing node user (UID 1000)
|
|
|
|
|
|
|
| 48 |
RUN mkdir -p /home/node/app /home/node/.openclaw && \
|
| 49 |
+
chown -R 1000:1000 /home/node
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
# Copy pre-built OpenClaw (skips npm install entirely — much faster!)
|
| 52 |
COPY --from=openclaw --chown=1000:1000 /app /home/node/.openclaw/openclaw-app
|
README.md
CHANGED
|
@@ -164,50 +164,6 @@ HuggingClaw automatically syncs your workspace (chats, settings, sessions) to a
|
|
| 164 |
| `HF_TOKEN` | — | HF token with **Write** access |
|
| 165 |
| `SYNC_INTERVAL` | `180` | Backup frequency in seconds |
|
| 166 |
|
| 167 |
-
## 📦 Ephemeral Package Re-install *(Optional)*
|
| 168 |
-
|
| 169 |
-
Yes — you can use extra packages after a Space restart without storing package files. The easiest option is to remember **one variable**:
|
| 170 |
-
|
| 171 |
-
| Variable | What to put in it |
|
| 172 |
-
| :--- | :--- |
|
| 173 |
-
| `HUGGINGCLAW_RUN` | Any bash commands you want to run on every startup |
|
| 174 |
-
|
| 175 |
-
Example:
|
| 176 |
-
|
| 177 |
-
```bash
|
| 178 |
-
HUGGINGCLAW_RUN="""
|
| 179 |
-
set -e
|
| 180 |
-
sudo apt-get update
|
| 181 |
-
sudo apt-get install -y ffmpeg
|
| 182 |
-
python3 -m pip install --user pandas requests
|
| 183 |
-
npm install -g typescript
|
| 184 |
-
"""
|
| 185 |
-
```
|
| 186 |
-
|
| 187 |
-
For very quote-heavy or strange scripts, put a base64 script in the same variable:
|
| 188 |
-
|
| 189 |
-
```bash
|
| 190 |
-
# locally
|
| 191 |
-
base64 -w0 setup.sh
|
| 192 |
-
|
| 193 |
-
# HF Variable
|
| 194 |
-
HUGGINGCLAW_RUN=base64:<paste-output-here>
|
| 195 |
-
```
|
| 196 |
-
|
| 197 |
-
How it works:
|
| 198 |
-
|
| 199 |
-
1. `HUGGINGCLAW_RUN` is run as a full bash script on every boot before the OpenClaw gateway launches, so multi-line commands, `if`, loops, functions, and heredocs work. Long installs will delay gateway startup.
|
| 200 |
-
2. Startup scripts load the same HuggingClaw shell wrappers as the interactive shell, so `apt install ...`, `pip install ...`, `npm install -g ...`, and `openclaw plugins install ...` behave consistently.
|
| 201 |
-
3. If you install from the OpenClaw shell manually, HuggingClaw records only successful install commands in `/home/node/.openclaw/workspace/startup.sh` for replay. Failed or dummy commands are not saved by the wrapper.
|
| 202 |
-
4. Package files are not persisted; commands are replayed to reconstruct them after restart.
|
| 203 |
-
|
| 204 |
-
Errors are always printed as `ERROR:` lines in Space logs. By default HuggingClaw logs the error and continues booting; set `HUGGINGCLAW_STARTUP_STRICT=true` if the Space should fail fast when any startup install command fails.
|
| 205 |
-
|
| 206 |
-
Advanced/backward-compatible variables still work if you prefer package-specific fields: `HUGGINGCLAW_APT_PACKAGES`, `HUGGINGCLAW_PIP_PACKAGES`, `HUGGINGCLAW_NPM_PACKAGES`, `HUGGINGCLAW_OPENCLAW_PLUGINS`, `HUGGINGCLAW_STARTUP_COMMANDS`, `HUGGINGCLAW_STARTUP_COMMAND_1`...`100`, `HUGGINGCLAW_STARTUP_SCRIPT`, and `HUGGINGCLAW_STARTUP_SCRIPT_B64`.
|
| 207 |
-
|
| 208 |
-
> [!IMPORTANT]
|
| 209 |
-
> `sudo` is available for package-manager commands only (`apt`, `apt-get`, and `dpkg`). This is enough for `sudo apt-get update` and `sudo apt-get install -y ...`, but it is not unrestricted root access. Apt-installed packages still disappear on Space restart, so put them in `HUGGINGCLAW_RUN` or let the shell wrapper record the command in `startup.sh`.
|
| 210 |
-
|
| 211 |
## 💓 Staying Alive *(Recommended on Free HF Spaces)*
|
| 212 |
|
| 213 |
Your Space will automatically be kept awake by a background Cloudflare Worker when you configure the `CLOUDFLARE_WORKERS_TOKEN` secret. The worker uses a cron trigger to regularly ping your Space's `/health` endpoint. The dashboard displays the current keep-alive worker status.
|
|
|
|
| 164 |
| `HF_TOKEN` | — | HF token with **Write** access |
|
| 165 |
| `SYNC_INTERVAL` | `180` | Backup frequency in seconds |
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
## 💓 Staying Alive *(Recommended on Free HF Spaces)*
|
| 168 |
|
| 169 |
Your Space will automatically be kept awake by a background Cloudflare Worker when you configure the `CLOUDFLARE_WORKERS_TOKEN` secret. The worker uses a cron trigger to regularly ping your Space's `/health` endpoint. The dashboard displays the current keep-alive worker status.
|
start.sh
CHANGED
|
@@ -129,20 +129,9 @@ mkdir -p /home/node/.openclaw/credentials
|
|
| 129 |
mkdir -p /home/node/.openclaw/memory
|
| 130 |
mkdir -p /home/node/.openclaw/extensions
|
| 131 |
mkdir -p /home/node/.openclaw/workspace
|
| 132 |
-
mkdir -p /home/node/.local/bin /home/node/.local/lib /home/node/.npm-global
|
| 133 |
chmod 700 /home/node/.openclaw
|
| 134 |
chmod 700 /home/node/.openclaw/credentials
|
| 135 |
|
| 136 |
-
# User-installed packages are intentionally ephemeral in the container. Keep
|
| 137 |
-
# npm/pip installs in user-writable locations, make apt noninteractive,
|
| 138 |
-
# and persist only a tiny replay script in the synced workspace so packages
|
| 139 |
-
# are re-installed after restart.
|
| 140 |
-
export NPM_CONFIG_PREFIX="${NPM_CONFIG_PREFIX:-/home/node/.local}"
|
| 141 |
-
export npm_config_prefix="$NPM_CONFIG_PREFIX"
|
| 142 |
-
export PYTHONUSERBASE="${PYTHONUSERBASE:-/home/node/.local}"
|
| 143 |
-
export DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}"
|
| 144 |
-
STARTUP_FILE="/home/node/.openclaw/workspace/startup.sh"
|
| 145 |
-
|
| 146 |
# ── Restore workspace/state from HF Dataset ──
|
| 147 |
BACKUP_DATASET="${BACKUP_DATASET_NAME:-huggingclaw-backup}"
|
| 148 |
if [ -n "${HF_TOKEN:-}" ]; then
|
|
@@ -548,167 +537,26 @@ if [ -n "${CLOUDFLARE_WORKERS_TOKEN:-}" ]; then
|
|
| 548 |
fi
|
| 549 |
|
| 550 |
# ── Write shell capture wrappers to .bashrc ──
|
| 551 |
-
|
| 552 |
-
# On the next boot the synced workspace/startup.sh replays those commands.
|
| 553 |
-
if [ ! -f "$STARTUP_FILE" ]; then
|
| 554 |
-
touch "$STARTUP_FILE"
|
| 555 |
-
chmod +x "$STARTUP_FILE"
|
| 556 |
-
echo "Created workspace/startup.sh"
|
| 557 |
-
fi
|
| 558 |
cat > /home/node/.bashrc << 'BASHRC'
|
| 559 |
-
export PATH="/home/node/.local/bin:$PATH"
|
| 560 |
-
export NPM_CONFIG_PREFIX="${NPM_CONFIG_PREFIX:-/home/node/.local}"
|
| 561 |
-
export npm_config_prefix="$NPM_CONFIG_PREFIX"
|
| 562 |
-
export PYTHONUSERBASE="${PYTHONUSERBASE:-/home/node/.local}"
|
| 563 |
-
export DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}"
|
| 564 |
STARTUP_FILE="/home/node/.openclaw/workspace/startup.sh"
|
| 565 |
_hc_append() {
|
| 566 |
local line="$*"
|
| 567 |
-
mkdir -p "$(dirname "$STARTUP_FILE")"
|
| 568 |
-
touch "$STARTUP_FILE"
|
| 569 |
-
chmod +x "$STARTUP_FILE" 2>/dev/null || true
|
| 570 |
grep -qxF "$line" "$STARTUP_FILE" 2>/dev/null || echo "$line" >> "$STARTUP_FILE"
|
| 571 |
}
|
| 572 |
-
_hc_quote_args() {
|
| 573 |
-
local quoted=()
|
| 574 |
-
local arg
|
| 575 |
-
for arg in "$@"; do
|
| 576 |
-
printf -v arg '%q' "$arg"
|
| 577 |
-
quoted+=("$arg")
|
| 578 |
-
done
|
| 579 |
-
printf '%s' "${quoted[*]}"
|
| 580 |
-
}
|
| 581 |
-
_hc_append_cmd() {
|
| 582 |
-
local cmd="$1"
|
| 583 |
-
shift
|
| 584 |
-
local args
|
| 585 |
-
args=$(_hc_quote_args "$@")
|
| 586 |
-
if [ -n "$args" ]; then
|
| 587 |
-
_hc_append "$cmd $args"
|
| 588 |
-
else
|
| 589 |
-
_hc_append "$cmd"
|
| 590 |
-
fi
|
| 591 |
-
}
|
| 592 |
-
_hc_has_arg() {
|
| 593 |
-
local needle="$1"
|
| 594 |
-
shift
|
| 595 |
-
local arg
|
| 596 |
-
for arg in "$@"; do
|
| 597 |
-
[ "$arg" = "$needle" ] && return 0
|
| 598 |
-
done
|
| 599 |
-
return 1
|
| 600 |
-
}
|
| 601 |
-
_hc_can_sudo_apt() {
|
| 602 |
-
command -v sudo >/dev/null 2>&1 && sudo -n apt-get --version >/dev/null 2>&1
|
| 603 |
-
}
|
| 604 |
-
_hc_apt_install() {
|
| 605 |
-
if [ "$(id -u)" -eq 0 ]; then
|
| 606 |
-
command apt-get update && command apt-get install -y "$@"
|
| 607 |
-
elif _hc_can_sudo_apt; then
|
| 608 |
-
sudo apt-get update && sudo apt-get install -y "$@"
|
| 609 |
-
else
|
| 610 |
-
echo "Error: apt install needs root. Rebuild with the latest HuggingClaw image or add packages to Dockerfile." >&2
|
| 611 |
-
return 1
|
| 612 |
-
fi
|
| 613 |
-
}
|
| 614 |
apt-get() {
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
shift
|
| 618 |
-
_hc_apt_install "$@"
|
| 619 |
-
local rc=$?
|
| 620 |
-
if [ $rc -eq 0 ]; then
|
| 621 |
-
_hc_append_cmd "sudo apt-get update && sudo apt-get install -y" "$@"
|
| 622 |
-
fi
|
| 623 |
-
return $rc
|
| 624 |
-
;;
|
| 625 |
-
update)
|
| 626 |
-
if [ "$(id -u)" -eq 0 ]; then
|
| 627 |
-
command apt-get "$@"
|
| 628 |
-
elif _hc_can_sudo_apt; then
|
| 629 |
-
sudo apt-get "$@"
|
| 630 |
-
else
|
| 631 |
-
command apt-get "$@"
|
| 632 |
-
fi
|
| 633 |
-
return $?
|
| 634 |
-
;;
|
| 635 |
-
*)
|
| 636 |
-
command apt-get "$@"
|
| 637 |
-
return $?
|
| 638 |
-
;;
|
| 639 |
-
esac
|
| 640 |
}
|
| 641 |
apt() {
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
shift
|
| 645 |
-
_hc_apt_install "$@"
|
| 646 |
-
local rc=$?
|
| 647 |
-
if [ $rc -eq 0 ]; then
|
| 648 |
-
_hc_append_cmd "sudo apt-get update && sudo apt-get install -y" "$@"
|
| 649 |
-
fi
|
| 650 |
-
return $rc
|
| 651 |
-
;;
|
| 652 |
-
update)
|
| 653 |
-
if [ "$(id -u)" -eq 0 ]; then
|
| 654 |
-
command apt "$@"
|
| 655 |
-
elif _hc_can_sudo_apt; then
|
| 656 |
-
sudo apt "$@"
|
| 657 |
-
else
|
| 658 |
-
command apt "$@"
|
| 659 |
-
fi
|
| 660 |
-
return $?
|
| 661 |
-
;;
|
| 662 |
-
*)
|
| 663 |
-
command apt "$@"
|
| 664 |
-
return $?
|
| 665 |
-
;;
|
| 666 |
-
esac
|
| 667 |
-
}
|
| 668 |
-
pip() {
|
| 669 |
-
if [ "${1:-}" = "install" ] && [ -z "${VIRTUAL_ENV:-}" ] && ! _hc_has_arg --user "$@" && ! _hc_has_arg --prefix "$@"; then
|
| 670 |
-
command pip install --user "${@:2}"
|
| 671 |
-
else
|
| 672 |
-
command pip "$@"
|
| 673 |
-
fi
|
| 674 |
-
local rc=$?
|
| 675 |
-
if [ $rc -eq 0 ] && [ "${1:-}" = "install" ]; then
|
| 676 |
-
_hc_append_cmd "python3 -m pip install --user" "${@:2}"
|
| 677 |
-
fi
|
| 678 |
-
return $rc
|
| 679 |
-
}
|
| 680 |
-
pip3() {
|
| 681 |
-
if [ "${1:-}" = "install" ] && [ -z "${VIRTUAL_ENV:-}" ] && ! _hc_has_arg --user "$@" && ! _hc_has_arg --prefix "$@"; then
|
| 682 |
-
command pip3 install --user "${@:2}"
|
| 683 |
-
else
|
| 684 |
-
command pip3 "$@"
|
| 685 |
-
fi
|
| 686 |
-
local rc=$?
|
| 687 |
-
if [ $rc -eq 0 ] && [ "${1:-}" = "install" ]; then
|
| 688 |
-
_hc_append_cmd "python3 -m pip install --user" "${@:2}"
|
| 689 |
-
fi
|
| 690 |
-
return $rc
|
| 691 |
-
}
|
| 692 |
-
npm() {
|
| 693 |
-
command npm "$@"
|
| 694 |
-
local rc=$?
|
| 695 |
-
if [ $rc -eq 0 ] && [ "${1:-}" = "install" ] && { [ "${2:-}" = "-g" ] || [ "${2:-}" = "--global" ]; }; then
|
| 696 |
-
_hc_append_cmd "npm install -g" "${@:3}"
|
| 697 |
-
fi
|
| 698 |
-
return $rc
|
| 699 |
-
}
|
| 700 |
-
openclaw() {
|
| 701 |
-
command openclaw "$@"
|
| 702 |
-
local rc=$?
|
| 703 |
-
if [ $rc -eq 0 ] && [ "${1:-}" = "plugins" ] && [ "${2:-}" = "install" ]; then
|
| 704 |
-
_hc_append_cmd "openclaw plugins install" "${@:3}"
|
| 705 |
-
fi
|
| 706 |
-
return $rc
|
| 707 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 708 |
BASHRC
|
| 709 |
-
cat > /home/node/.profile <<'PROFILE'
|
| 710 |
-
[ -f ~/.bashrc ] && . ~/.bashrc
|
| 711 |
-
PROFILE
|
| 712 |
echo "Shell capture wrappers ready."
|
| 713 |
|
| 714 |
# ── Re-install previously installed plugins ──
|
|
@@ -732,199 +580,6 @@ if [ -f "$EXISTING_CONFIG" ]; then
|
|
| 732 |
fi
|
| 733 |
fi
|
| 734 |
|
| 735 |
-
# ── Startup command runner ──
|
| 736 |
-
# Runs user-provided boot commands one by one so failures are visible in logs.
|
| 737 |
-
# By default failures are logged and boot continues; set
|
| 738 |
-
# HUGGINGCLAW_STARTUP_STRICT=true to fail the Space startup on any error.
|
| 739 |
-
HC_STARTUP_FAILURES=0
|
| 740 |
-
HC_STARTUP_STRICT_NORMALIZED=$(printf '%s' "${HUGGINGCLAW_STARTUP_STRICT:-false}" | tr '[:upper:]' '[:lower:]')
|
| 741 |
-
hc_run_startup_command() {
|
| 742 |
-
local source_label="$1"
|
| 743 |
-
local command_text="$2"
|
| 744 |
-
[ -n "$command_text" ] || return 0
|
| 745 |
-
|
| 746 |
-
echo "[startup:${source_label}] $command_text"
|
| 747 |
-
set +e
|
| 748 |
-
bash -lc "$command_text"
|
| 749 |
-
local rc=$?
|
| 750 |
-
set -e
|
| 751 |
-
if [ "$rc" -eq 0 ]; then
|
| 752 |
-
echo "[startup:${source_label}] ok"
|
| 753 |
-
return 0
|
| 754 |
-
fi
|
| 755 |
-
|
| 756 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 757 |
-
echo "ERROR: startup command failed (${source_label}, exit ${rc}): $command_text" >&2
|
| 758 |
-
return "$rc"
|
| 759 |
-
}
|
| 760 |
-
|
| 761 |
-
hc_run_startup_script() {
|
| 762 |
-
local source_label="$1"
|
| 763 |
-
local script_text="$2"
|
| 764 |
-
[ -n "$script_text" ] || return 0
|
| 765 |
-
|
| 766 |
-
local script_file
|
| 767 |
-
script_file=$(mktemp "/tmp/huggingclaw-startup-${source_label//[^A-Za-z0-9_.-]/_}.XXXXXX.sh")
|
| 768 |
-
{
|
| 769 |
-
# Load HuggingClaw's install wrappers for env-provided scripts too, so
|
| 770 |
-
# `apt install`, `pip install`, `npm install -g`, and OpenClaw plugin
|
| 771 |
-
# installs behave the same way as they do in the interactive shell.
|
| 772 |
-
echo '[ -f /home/node/.bashrc ] && . /home/node/.bashrc'
|
| 773 |
-
printf '%s\n' "$script_text"
|
| 774 |
-
} > "$script_file"
|
| 775 |
-
chmod 700 "$script_file"
|
| 776 |
-
|
| 777 |
-
echo "[startup:${source_label}] running script (${script_file})"
|
| 778 |
-
set +e
|
| 779 |
-
bash "$script_file"
|
| 780 |
-
local rc=$?
|
| 781 |
-
set -e
|
| 782 |
-
rm -f "$script_file"
|
| 783 |
-
|
| 784 |
-
if [ "$rc" -eq 0 ]; then
|
| 785 |
-
echo "[startup:${source_label}] ok"
|
| 786 |
-
return 0
|
| 787 |
-
fi
|
| 788 |
-
|
| 789 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 790 |
-
echo "ERROR: startup script failed (${source_label}, exit ${rc})" >&2
|
| 791 |
-
return "$rc"
|
| 792 |
-
}
|
| 793 |
-
hc_run_startup_script_b64() {
|
| 794 |
-
local source_label="$1"
|
| 795 |
-
local encoded_script="$2"
|
| 796 |
-
[ -n "$encoded_script" ] || return 0
|
| 797 |
-
|
| 798 |
-
local script_text
|
| 799 |
-
if ! script_text=$(printf '%s' "$encoded_script" | base64 -d 2>/dev/null); then
|
| 800 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 801 |
-
echo "ERROR: startup script base64 decode failed (${source_label})" >&2
|
| 802 |
-
return 1
|
| 803 |
-
fi
|
| 804 |
-
|
| 805 |
-
hc_run_startup_script "$source_label" "$script_text"
|
| 806 |
-
}
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
hc_run_startup_auto() {
|
| 810 |
-
local source_label="$1"
|
| 811 |
-
local payload="$2"
|
| 812 |
-
[ -n "$payload" ] || return 0
|
| 813 |
-
|
| 814 |
-
if [[ "$payload" == base64:* ]]; then
|
| 815 |
-
hc_run_startup_script_b64 "$source_label" "${payload#base64:}"
|
| 816 |
-
elif [[ "$payload" == b64:* ]]; then
|
| 817 |
-
hc_run_startup_script_b64 "$source_label" "${payload#b64:}"
|
| 818 |
-
else
|
| 819 |
-
hc_run_startup_script "$source_label" "$payload"
|
| 820 |
-
fi
|
| 821 |
-
}
|
| 822 |
-
|
| 823 |
-
hc_run_command_block() {
|
| 824 |
-
local source_label="$1"
|
| 825 |
-
local command_block="$2"
|
| 826 |
-
local line
|
| 827 |
-
local index=0
|
| 828 |
-
|
| 829 |
-
while IFS= read -r line || [ -n "$line" ]; do
|
| 830 |
-
# Skip blank lines and comments so multi-line env vars can be documented.
|
| 831 |
-
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
|
| 832 |
-
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
| 833 |
-
|
| 834 |
-
index=$((index + 1))
|
| 835 |
-
hc_run_startup_command "${source_label}[${index}]" "$line" || true
|
| 836 |
-
done <<< "$command_block"
|
| 837 |
-
}
|
| 838 |
-
hc_finish_startup_commands() {
|
| 839 |
-
if [ "$HC_STARTUP_FAILURES" -gt 0 ]; then
|
| 840 |
-
echo "ERROR: ${HC_STARTUP_FAILURES} startup command(s) failed. Check the log lines above." >&2
|
| 841 |
-
if [ "$HC_STARTUP_STRICT_NORMALIZED" = "true" ] || [ "$HC_STARTUP_STRICT_NORMALIZED" = "1" ] || [ "$HC_STARTUP_STRICT_NORMALIZED" = "yes" ]; then
|
| 842 |
-
echo "ERROR: HUGGINGCLAW_STARTUP_STRICT=true, stopping startup." >&2
|
| 843 |
-
exit 1
|
| 844 |
-
fi
|
| 845 |
-
fi
|
| 846 |
-
return 0
|
| 847 |
-
}
|
| 848 |
-
|
| 849 |
-
# ── Optional package install lists from HF Variables/Secrets ──
|
| 850 |
-
# These install package names every boot without persisting package files.
|
| 851 |
-
# Use them when you prefer HF Variables over editing workspace/startup.sh.
|
| 852 |
-
if [ -n "${HUGGINGCLAW_APT_PACKAGES:-}" ]; then
|
| 853 |
-
echo "Installing apt packages from HUGGINGCLAW_APT_PACKAGES..."
|
| 854 |
-
read -r -a HC_APT_PACKAGES <<< "$HUGGINGCLAW_APT_PACKAGES"
|
| 855 |
-
if command -v sudo >/dev/null 2>&1; then
|
| 856 |
-
if sudo apt-get update && sudo apt-get install -y "${HC_APT_PACKAGES[@]}"; then
|
| 857 |
-
echo "HUGGINGCLAW_APT_PACKAGES install complete."
|
| 858 |
-
else
|
| 859 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 860 |
-
echo "ERROR: HUGGINGCLAW_APT_PACKAGES install failed: ${HUGGINGCLAW_APT_PACKAGES}" >&2
|
| 861 |
-
fi
|
| 862 |
-
else
|
| 863 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 864 |
-
echo "ERROR: sudo is unavailable; HUGGINGCLAW_APT_PACKAGES install skipped" >&2
|
| 865 |
-
fi
|
| 866 |
-
fi
|
| 867 |
-
if [ -n "${HUGGINGCLAW_PIP_PACKAGES:-}" ]; then
|
| 868 |
-
echo "Installing Python packages from HUGGINGCLAW_PIP_PACKAGES..."
|
| 869 |
-
read -r -a HC_PIP_PACKAGES <<< "$HUGGINGCLAW_PIP_PACKAGES"
|
| 870 |
-
if python3 -m pip install --user "${HC_PIP_PACKAGES[@]}"; then
|
| 871 |
-
echo "HUGGINGCLAW_PIP_PACKAGES install complete."
|
| 872 |
-
else
|
| 873 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 874 |
-
echo "ERROR: HUGGINGCLAW_PIP_PACKAGES install failed: ${HUGGINGCLAW_PIP_PACKAGES}" >&2
|
| 875 |
-
fi
|
| 876 |
-
fi
|
| 877 |
-
if [ -n "${HUGGINGCLAW_NPM_PACKAGES:-}" ]; then
|
| 878 |
-
echo "Installing global npm packages from HUGGINGCLAW_NPM_PACKAGES..."
|
| 879 |
-
read -r -a HC_NPM_PACKAGES <<< "$HUGGINGCLAW_NPM_PACKAGES"
|
| 880 |
-
if npm install -g "${HC_NPM_PACKAGES[@]}"; then
|
| 881 |
-
echo "HUGGINGCLAW_NPM_PACKAGES install complete."
|
| 882 |
-
else
|
| 883 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 884 |
-
echo "ERROR: HUGGINGCLAW_NPM_PACKAGES install failed: ${HUGGINGCLAW_NPM_PACKAGES}" >&2
|
| 885 |
-
fi
|
| 886 |
-
fi
|
| 887 |
-
if [ -n "${HUGGINGCLAW_OPENCLAW_PLUGINS:-}" ]; then
|
| 888 |
-
echo "Installing OpenClaw plugins from HUGGINGCLAW_OPENCLAW_PLUGINS..."
|
| 889 |
-
read -r -a HC_OPENCLAW_PLUGINS <<< "$HUGGINGCLAW_OPENCLAW_PLUGINS"
|
| 890 |
-
if openclaw plugins install "${HC_OPENCLAW_PLUGINS[@]}"; then
|
| 891 |
-
echo "HUGGINGCLAW_OPENCLAW_PLUGINS install complete."
|
| 892 |
-
else
|
| 893 |
-
HC_STARTUP_FAILURES=$((HC_STARTUP_FAILURES + 1))
|
| 894 |
-
echo "ERROR: HUGGINGCLAW_OPENCLAW_PLUGINS install failed: ${HUGGINGCLAW_OPENCLAW_PLUGINS}" >&2
|
| 895 |
-
fi
|
| 896 |
-
fi
|
| 897 |
-
|
| 898 |
-
# ── Arbitrary startup commands from HF Variables/Secrets ──
|
| 899 |
-
# Recommended: use one variable, HUGGINGCLAW_RUN, as a full bash script. If the
|
| 900 |
-
# value starts with base64: or b64:, the rest is decoded and run as the script.
|
| 901 |
-
# Legacy granular HUGGINGCLAW_STARTUP_* variables are still supported below.
|
| 902 |
-
if [ -n "${HUGGINGCLAW_RUN:-}" ]; then
|
| 903 |
-
hc_run_startup_auto "HUGGINGCLAW_RUN" "$HUGGINGCLAW_RUN" || true
|
| 904 |
-
fi
|
| 905 |
-
if [ -n "${HUGGINGCLAW_STARTUP_COMMANDS:-}" ]; then
|
| 906 |
-
echo "Running commands from HUGGINGCLAW_STARTUP_COMMANDS..."
|
| 907 |
-
hc_run_command_block "HUGGINGCLAW_STARTUP_COMMANDS" "$HUGGINGCLAW_STARTUP_COMMANDS"
|
| 908 |
-
fi
|
| 909 |
-
for HC_STARTUP_INDEX in $(seq 1 100); do
|
| 910 |
-
HC_STARTUP_VAR="HUGGINGCLAW_STARTUP_COMMAND_${HC_STARTUP_INDEX}"
|
| 911 |
-
if [ -n "${!HC_STARTUP_VAR:-}" ]; then
|
| 912 |
-
hc_run_startup_command "$HC_STARTUP_VAR" "${!HC_STARTUP_VAR}" || true
|
| 913 |
-
fi
|
| 914 |
-
done
|
| 915 |
-
if [ -n "${HUGGINGCLAW_STARTUP_SCRIPT:-}" ]; then
|
| 916 |
-
hc_run_startup_script "HUGGINGCLAW_STARTUP_SCRIPT" "$HUGGINGCLAW_STARTUP_SCRIPT" || true
|
| 917 |
-
fi
|
| 918 |
-
if [ -n "${HUGGINGCLAW_STARTUP_SCRIPT_B64:-}" ]; then
|
| 919 |
-
hc_run_startup_script_b64 "HUGGINGCLAW_STARTUP_SCRIPT_B64" "$HUGGINGCLAW_STARTUP_SCRIPT_B64" || true
|
| 920 |
-
fi
|
| 921 |
-
for HC_STARTUP_INDEX in $(seq 1 20); do
|
| 922 |
-
HC_STARTUP_VAR="HUGGINGCLAW_STARTUP_SCRIPT_B64_${HC_STARTUP_INDEX}"
|
| 923 |
-
if [ -n "${!HC_STARTUP_VAR:-}" ]; then
|
| 924 |
-
hc_run_startup_script_b64 "$HC_STARTUP_VAR" "${!HC_STARTUP_VAR}" || true
|
| 925 |
-
fi
|
| 926 |
-
done
|
| 927 |
-
|
| 928 |
# ── Run workspace startup script ──
|
| 929 |
STARTUP_FILE="/home/node/.openclaw/workspace/startup.sh"
|
| 930 |
if [ ! -f "$STARTUP_FILE" ]; then
|
|
@@ -933,11 +588,10 @@ if [ ! -f "$STARTUP_FILE" ]; then
|
|
| 933 |
echo "Created workspace/startup.sh"
|
| 934 |
fi
|
| 935 |
if [ -s "$STARTUP_FILE" ]; then
|
| 936 |
-
echo "Running workspace/startup.sh
|
| 937 |
-
|
| 938 |
-
echo "
|
| 939 |
fi
|
| 940 |
-
hc_finish_startup_commands
|
| 941 |
|
| 942 |
# ── Launch gateway ──
|
| 943 |
echo "Launching OpenClaw gateway on port 7860..."
|
|
|
|
| 129 |
mkdir -p /home/node/.openclaw/memory
|
| 130 |
mkdir -p /home/node/.openclaw/extensions
|
| 131 |
mkdir -p /home/node/.openclaw/workspace
|
|
|
|
| 132 |
chmod 700 /home/node/.openclaw
|
| 133 |
chmod 700 /home/node/.openclaw/credentials
|
| 134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
# ── Restore workspace/state from HF Dataset ──
|
| 136 |
BACKUP_DATASET="${BACKUP_DATASET_NAME:-huggingclaw-backup}"
|
| 137 |
if [ -n "${HF_TOKEN:-}" ]; then
|
|
|
|
| 537 |
fi
|
| 538 |
|
| 539 |
# ── Write shell capture wrappers to .bashrc ──
|
| 540 |
+
STARTUP_FILE="/home/node/.openclaw/workspace/startup.sh"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
cat > /home/node/.bashrc << 'BASHRC'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
STARTUP_FILE="/home/node/.openclaw/workspace/startup.sh"
|
| 543 |
_hc_append() {
|
| 544 |
local line="$*"
|
|
|
|
|
|
|
|
|
|
| 545 |
grep -qxF "$line" "$STARTUP_FILE" 2>/dev/null || echo "$line" >> "$STARTUP_FILE"
|
| 546 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
apt-get() {
|
| 548 |
+
command apt-get "$@"
|
| 549 |
+
[[ "$1" == "install" ]] && _hc_append "apt-get install -y ${@:2}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 550 |
}
|
| 551 |
apt() {
|
| 552 |
+
command apt "$@"
|
| 553 |
+
[[ "$1" == "install" ]] && _hc_append "apt-get install -y ${@:2}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
}
|
| 555 |
+
pip() { command pip "$@"; [[ "$1" == "install" ]] && _hc_append "pip install ${@:2}"; }
|
| 556 |
+
pip3() { command pip3 "$@"; [[ "$1" == "install" ]] && _hc_append "pip3 install ${@:2}"; }
|
| 557 |
+
npm() { command npm "$@"; [[ "$1" == "install" && "$2" == "-g" ]] && _hc_append "npm install -g ${@:3}"; }
|
| 558 |
+
openclaw() { command openclaw "$@"; [[ "$1" == "plugins" && "$2" == "install" ]] && _hc_append "openclaw plugins install ${@:3}"; }
|
| 559 |
BASHRC
|
|
|
|
|
|
|
|
|
|
| 560 |
echo "Shell capture wrappers ready."
|
| 561 |
|
| 562 |
# ── Re-install previously installed plugins ──
|
|
|
|
| 580 |
fi
|
| 581 |
fi
|
| 582 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
# ── Run workspace startup script ──
|
| 584 |
STARTUP_FILE="/home/node/.openclaw/workspace/startup.sh"
|
| 585 |
if [ ! -f "$STARTUP_FILE" ]; then
|
|
|
|
| 588 |
echo "Created workspace/startup.sh"
|
| 589 |
fi
|
| 590 |
if [ -s "$STARTUP_FILE" ]; then
|
| 591 |
+
echo "Running workspace/startup.sh..."
|
| 592 |
+
bash "$STARTUP_FILE" || echo "Warning: startup.sh had errors, continuing..."
|
| 593 |
+
echo "Startup script complete."
|
| 594 |
fi
|
|
|
|
| 595 |
|
| 596 |
# ── Launch gateway ──
|
| 597 |
echo "Launching OpenClaw gateway on port 7860..."
|