Spaces:
Running
fix: center login form, simplify startup logs
Browse fileslogin.html:
- Fix token input/button centering (remove Bootstrap navbar pull-left)
- Update token hint: GATEWAY_TOKEN is default, JUPYTER_TOKEN overrides
- Remove stale "default token is huggingface" text
start.sh:
- Silence GATEWAY_TOKEN auto-enable message (was noisy debug output)
- Redirect pip install output to /dev/null, show one-liner status
- Remove "JUPYTER_TOKEN not set β using GATEWAY_TOKEN" noise
- Replace "DEV_MODE enabled (false) β starting JupyterLab..." with clean
"Terminal : starting / started / installed" format
- Remove "Jupyter terminal disabled for this boot" noise
- Remove "Private :" line from startup summary
- Clean DevData restore messages to match consistent format
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- login.html +12 -22
- start.sh +11 -17
|
@@ -8,36 +8,26 @@
|
|
| 8 |
<img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face Logo" style="max-width: 120px; margin-bottom: 24px;">
|
| 9 |
<h3>HuggingClaw Terminal</h3>
|
| 10 |
<h4>Welcome to JupyterLab</h4>
|
| 11 |
-
<
|
| 12 |
-
<p style="color:#666;">This terminal is mounted at <code>/terminal/</code> inside the same Hugging Face Space as the OpenClaw UI.</p>
|
| 13 |
|
| 14 |
{% if login_available %}
|
| 15 |
-
<div
|
| 16 |
-
<
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
{% endif %}
|
| 27 |
-
<input type="password" name="password" id="password_input" class="form-control">
|
| 28 |
-
<button type="submit" class="btn btn-default" id="login_submit">{% trans %}Log in{% endtrans %}</button>
|
| 29 |
-
</form>
|
| 30 |
-
</div>
|
| 31 |
-
</div>
|
| 32 |
-
</div>
|
| 33 |
-
</div>
|
| 34 |
</div>
|
| 35 |
{% else %}
|
| 36 |
<p>{% trans %}No login available, you shouldn't be seeing this page.{% endtrans %}</p>
|
| 37 |
{% endif %}
|
| 38 |
|
| 39 |
<h5 style="margin-top:28px;"><a href="/dashboard">Back to HuggingClaw dashboard</a></h5>
|
| 40 |
-
<p>This login page is based on the Hugging Face JupyterLab Space template.</p>
|
| 41 |
|
| 42 |
{% if message %}
|
| 43 |
<div class="row">
|
|
|
|
| 8 |
<img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face Logo" style="max-width: 120px; margin-bottom: 24px;">
|
| 9 |
<h3>HuggingClaw Terminal</h3>
|
| 10 |
<h4>Welcome to JupyterLab</h4>
|
| 11 |
+
<p style="color:#666;">Token defaults to your <code>GATEWAY_TOKEN</code>. Set <code>JUPYTER_TOKEN</code> to override.</p>
|
|
|
|
| 12 |
|
| 13 |
{% if login_available %}
|
| 14 |
+
<div style="display:flex; justify-content:center; margin-top:24px;">
|
| 15 |
+
<form action="{{base_url}}login?next={{next}}" method="post" style="display:flex; align-items:center; gap:8px;">
|
| 16 |
+
{{ xsrf_form_html() | safe }}
|
| 17 |
+
{% if token_available %}
|
| 18 |
+
<label for="password_input"><strong>{% trans %}Jupyter token <span title="Your GATEWAY_TOKEN (or JUPYTER_TOKEN if set)">β</span>{% endtrans %}</strong></label>
|
| 19 |
+
{% else %}
|
| 20 |
+
<label for="password_input"><strong>{% trans %}Jupyter password:{% endtrans %}</strong></label>
|
| 21 |
+
{% endif %}
|
| 22 |
+
<input type="password" name="password" id="password_input" class="form-control">
|
| 23 |
+
<button type="submit" class="btn btn-default" id="login_submit">{% trans %}Log in{% endtrans %}</button>
|
| 24 |
+
</form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
</div>
|
| 26 |
{% else %}
|
| 27 |
<p>{% trans %}No login available, you shouldn't be seeing this page.{% endtrans %}</p>
|
| 28 |
{% endif %}
|
| 29 |
|
| 30 |
<h5 style="margin-top:28px;"><a href="/dashboard">Back to HuggingClaw dashboard</a></h5>
|
|
|
|
| 31 |
|
| 32 |
{% if message %}
|
| 33 |
<div class="row">
|
|
@@ -94,7 +94,7 @@ fi
|
|
| 94 |
# GATEWAY_TOKEN doubles as JUPYTER_TOKEN (see start_jupyter_once) β no extra secret required.
|
| 95 |
if [ "$DEV_MODE_ENABLED" != "true" ] && [ -z "${DEV_MODE:-}" ] && [ -n "${GATEWAY_TOKEN:-}" ]; then
|
| 96 |
DEV_MODE_ENABLED=true
|
| 97 |
-
|
| 98 |
fi
|
| 99 |
SYNC_INTERVAL="$(trim_var "${SYNC_INTERVAL:-180}")"
|
| 100 |
DEVDATA_DATASET_NAME="$(trim_var "${DEVDATA_DATASET_NAME:-huggingclaw-devdata}")"
|
|
@@ -768,12 +768,12 @@ export PATH="$HOME/.local/bin:$PATH"
|
|
| 768 |
|
| 769 |
# Runtime install fallback: only attempt if DEV_MODE is enabled but install failed during build
|
| 770 |
if [ "$DEV_MODE_ENABLED" = "true" ] && ! python3 -c "import jupyterlab" >/dev/null 2>&1; then
|
| 771 |
-
echo "
|
| 772 |
-
if python3 -m pip install --user --no-cache-dir --break-system-packages "jupyterlab>=4.2,<5" "tornado>=6.3" "ipywidgets>=8.1"; then
|
| 773 |
-
echo "
|
| 774 |
python3 -c "from pathlib import Path; import shutil, jupyter_server; d=Path(jupyter_server.__file__).parent/'templates'; d.mkdir(parents=True,exist_ok=True); shutil.copyfile('/home/node/app/login.html', d/'login.html')" || true
|
| 775 |
else
|
| 776 |
-
echo "
|
| 777 |
RUNTIME_JUPYTER_ENABLED=false
|
| 778 |
fi
|
| 779 |
fi
|
|
@@ -789,7 +789,6 @@ if [ -n "${SPACE_HOST:-}" ]; then
|
|
| 789 |
else
|
| 790 |
echo "Routes : /app/ (Control UI)"
|
| 791 |
fi
|
| 792 |
-
echo "Private : open the Hugging Face App tab first; raw https://${SPACE_HOST}/... links can show HF 404 without the embedded Space session."
|
| 793 |
fi
|
| 794 |
echo ""
|
| 795 |
|
|
@@ -858,7 +857,6 @@ start_jupyter_once() {
|
|
| 858 |
# reuse GATEWAY_TOKEN. Both protect the same Space, so the credential is equivalent.
|
| 859 |
if { [ -z "${JUPYTER_TOKEN:-}" ] || [ "${JUPYTER_TOKEN}" = "huggingface" ]; } && [ -n "${GATEWAY_TOKEN:-}" ]; then
|
| 860 |
JUPYTER_TOKEN="$GATEWAY_TOKEN"
|
| 861 |
-
echo "JUPYTER_TOKEN not set β using GATEWAY_TOKEN as terminal auth token"
|
| 862 |
fi
|
| 863 |
|
| 864 |
# Security guard: refuse to start JupyterLab with the insecure default token.
|
|
@@ -891,7 +889,7 @@ start_jupyter_once() {
|
|
| 891 |
# Pre-create runtime directory
|
| 892 |
mkdir -p "$JUPYTER_ROOT_DIR/.jupyter"
|
| 893 |
|
| 894 |
-
echo "
|
| 895 |
JUPYTER_LOG_FILE="/tmp/jupyterlab.log"
|
| 896 |
|
| 897 |
# Use explicit Python to avoid PATH issues; set memory-friendly limits
|
|
@@ -917,7 +915,7 @@ start_jupyter_once() {
|
|
| 917 |
>> "$JUPYTER_LOG_FILE" 2>&1 &
|
| 918 |
JUPYTER_PID=$!
|
| 919 |
export JUPYTER_PID
|
| 920 |
-
echo "
|
| 921 |
}
|
| 922 |
|
| 923 |
# BUG FIX #3: DevData restore must happen BEFORE JupyterLab starts.
|
|
@@ -930,9 +928,9 @@ if [ "$RUNTIME_JUPYTER_ENABLED" = "true" ] && \
|
|
| 930 |
[ -n "${HF_TOKEN:-}" ] && \
|
| 931 |
[ -f "/home/node/app/jupyter-devdata-sync.py" ] && \
|
| 932 |
[ "${DEVDATA_DATASET_NAME:-huggingclaw-devdata}" != "${BACKUP_DATASET_NAME:-huggingclaw-backup}" ]; then
|
| 933 |
-
echo "DevData
|
| 934 |
-
python3 /home/node/app/jupyter-devdata-sync.py --restore || \
|
| 935 |
-
echo "DevData
|
| 936 |
fi
|
| 937 |
|
| 938 |
# Fix: reinstall jsonschema AFTER devdata restore β restore can overwrite a broken
|
|
@@ -940,9 +938,7 @@ fi
|
|
| 940 |
# JupyterLab to crash with a circular import error on every boot.
|
| 941 |
if [ "$DEV_MODE_ENABLED" = "true" ]; then
|
| 942 |
if ! python3 -c "import jsonschema" >/dev/null 2>&1; then
|
| 943 |
-
|
| 944 |
-
python3 -m pip install --force-reinstall --no-cache-dir --break-system-packages "jsonschema>=4.0" >/dev/null 2>&1 || true
|
| 945 |
-
echo "DevData : jsonschema reinstall done."
|
| 946 |
fi
|
| 947 |
fi
|
| 948 |
|
|
@@ -950,8 +946,6 @@ fi
|
|
| 950 |
# Accessible via /terminal/ path through the health-server proxy
|
| 951 |
if [ "$RUNTIME_JUPYTER_ENABLED" = "true" ]; then
|
| 952 |
start_jupyter_once
|
| 953 |
-
else
|
| 954 |
-
echo "Jupyter terminal disabled for this boot (DEV_MODE=${DEV_MODE_RAW})."
|
| 955 |
fi
|
| 956 |
|
| 957 |
if [ -n "${CLOUDFLARE_WORKERS_TOKEN:-}" ]; then
|
|
|
|
| 94 |
# GATEWAY_TOKEN doubles as JUPYTER_TOKEN (see start_jupyter_once) β no extra secret required.
|
| 95 |
if [ "$DEV_MODE_ENABLED" != "true" ] && [ -z "${DEV_MODE:-}" ] && [ -n "${GATEWAY_TOKEN:-}" ]; then
|
| 96 |
DEV_MODE_ENABLED=true
|
| 97 |
+
: # auto-enable is silent; set DEV_MODE=false to opt out
|
| 98 |
fi
|
| 99 |
SYNC_INTERVAL="$(trim_var "${SYNC_INTERVAL:-180}")"
|
| 100 |
DEVDATA_DATASET_NAME="$(trim_var "${DEVDATA_DATASET_NAME:-huggingclaw-devdata}")"
|
|
|
|
| 768 |
|
| 769 |
# Runtime install fallback: only attempt if DEV_MODE is enabled but install failed during build
|
| 770 |
if [ "$DEV_MODE_ENABLED" = "true" ] && ! python3 -c "import jupyterlab" >/dev/null 2>&1; then
|
| 771 |
+
echo "Terminal : installing JupyterLab..."
|
| 772 |
+
if python3 -m pip install -q --user --no-cache-dir --break-system-packages "jupyterlab>=4.2,<5" "tornado>=6.3" "ipywidgets>=8.1" >/dev/null 2>&1; then
|
| 773 |
+
echo "Terminal : installed"
|
| 774 |
python3 -c "from pathlib import Path; import shutil, jupyter_server; d=Path(jupyter_server.__file__).parent/'templates'; d.mkdir(parents=True,exist_ok=True); shutil.copyfile('/home/node/app/login.html', d/'login.html')" || true
|
| 775 |
else
|
| 776 |
+
echo "Terminal : install failed β disabling for this boot"
|
| 777 |
RUNTIME_JUPYTER_ENABLED=false
|
| 778 |
fi
|
| 779 |
fi
|
|
|
|
| 789 |
else
|
| 790 |
echo "Routes : /app/ (Control UI)"
|
| 791 |
fi
|
|
|
|
| 792 |
fi
|
| 793 |
echo ""
|
| 794 |
|
|
|
|
| 857 |
# reuse GATEWAY_TOKEN. Both protect the same Space, so the credential is equivalent.
|
| 858 |
if { [ -z "${JUPYTER_TOKEN:-}" ] || [ "${JUPYTER_TOKEN}" = "huggingface" ]; } && [ -n "${GATEWAY_TOKEN:-}" ]; then
|
| 859 |
JUPYTER_TOKEN="$GATEWAY_TOKEN"
|
|
|
|
| 860 |
fi
|
| 861 |
|
| 862 |
# Security guard: refuse to start JupyterLab with the insecure default token.
|
|
|
|
| 889 |
# Pre-create runtime directory
|
| 890 |
mkdir -p "$JUPYTER_ROOT_DIR/.jupyter"
|
| 891 |
|
| 892 |
+
echo "Terminal : starting (root: $JUPYTER_ROOT_DIR)"
|
| 893 |
JUPYTER_LOG_FILE="/tmp/jupyterlab.log"
|
| 894 |
|
| 895 |
# Use explicit Python to avoid PATH issues; set memory-friendly limits
|
|
|
|
| 915 |
>> "$JUPYTER_LOG_FILE" 2>&1 &
|
| 916 |
JUPYTER_PID=$!
|
| 917 |
export JUPYTER_PID
|
| 918 |
+
echo "Terminal : started (PID: $JUPYTER_PID)"
|
| 919 |
}
|
| 920 |
|
| 921 |
# BUG FIX #3: DevData restore must happen BEFORE JupyterLab starts.
|
|
|
|
| 928 |
[ -n "${HF_TOKEN:-}" ] && \
|
| 929 |
[ -f "/home/node/app/jupyter-devdata-sync.py" ] && \
|
| 930 |
[ "${DEVDATA_DATASET_NAME:-huggingclaw-devdata}" != "${BACKUP_DATASET_NAME:-huggingclaw-backup}" ]; then
|
| 931 |
+
echo "DevData : restoring workspace..."
|
| 932 |
+
python3 /home/node/app/jupyter-devdata-sync.py --restore 2>/dev/null || \
|
| 933 |
+
echo "DevData : restore warning (non-fatal); continuing startup."
|
| 934 |
fi
|
| 935 |
|
| 936 |
# Fix: reinstall jsonschema AFTER devdata restore β restore can overwrite a broken
|
|
|
|
| 938 |
# JupyterLab to crash with a circular import error on every boot.
|
| 939 |
if [ "$DEV_MODE_ENABLED" = "true" ]; then
|
| 940 |
if ! python3 -c "import jsonschema" >/dev/null 2>&1; then
|
| 941 |
+
python3 -m pip install -q --force-reinstall --no-cache-dir --break-system-packages "jsonschema>=4.0" >/dev/null 2>&1 || true
|
|
|
|
|
|
|
| 942 |
fi
|
| 943 |
fi
|
| 944 |
|
|
|
|
| 946 |
# Accessible via /terminal/ path through the health-server proxy
|
| 947 |
if [ "$RUNTIME_JUPYTER_ENABLED" = "true" ]; then
|
| 948 |
start_jupyter_once
|
|
|
|
|
|
|
| 949 |
fi
|
| 950 |
|
| 951 |
if [ -n "${CLOUDFLARE_WORKERS_TOKEN:-}" ]; then
|