fix: improve SSH service stability and backup.py error handling
Browse files- README.md +114 -52
- README_zh.md +120 -26
- scripts/hf-entrypoint.sh +1 -1
- scripts/rebuild-space.sh +171 -0
README.md
CHANGED
|
@@ -24,18 +24,44 @@ This setup is designed to provide the following:
|
|
| 24 |
- Store OpenClaw config/workspace under `/root/.openclaw`
|
| 25 |
- Restore state automatically from a Hugging Face Dataset on startup
|
| 26 |
- Run scheduled backups of OpenClaw data to a Hugging Face Dataset via `cron` (as `root` user)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
- Preinstall `python3`, `uv`, `vim`, `neovim`, `chromium` (via Chrome for Testing archive), `gh`, `hf`, `opencode`, `codex`, `claude` (Claude Code CLI), `@larksuite/cli` (with `npx skills add larksuite/cli -y -g`), and `sshx` in the image for interactive terminal use
|
| 28 |
|
| 29 |
## Repository Layout
|
| 30 |
|
| 31 |
- `Dockerfile`: Runtime image for the Space
|
| 32 |
- `scripts/openclaw-entrypoint.sh`: Main startup flow (restore, config generation, cron setup, gateway start)
|
| 33 |
-
- `
|
|
|
|
|
|
|
| 34 |
- `scripts/openclaw-backup-cron.sh`: Cron entrypoint for backup jobs
|
|
|
|
|
|
|
| 35 |
- `scripts/openclaw-restore.sh`: Startup restore entrypoint
|
| 36 |
-
- `scripts/openclaw-gateway-
|
|
|
|
|
|
|
|
|
|
| 37 |
- `scripts/bootstrap-hf.sh`: Interactive bootstrap for Space/Dataset creation, upload, and Space variables/secrets setup (macOS/Linux)
|
| 38 |
- `scripts/bootstrap-hf.ps1`: Interactive bootstrap for Space/Dataset creation, upload, and Space variables/secrets setup (Windows PowerShell)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
- `tests/test_backup.py`: Unit tests for the backup module
|
| 40 |
- `tests/test_entrypoint_config.py`: Unit tests for gateway config generation behavior
|
| 41 |
|
|
@@ -63,31 +89,36 @@ In that case, you can still configure from inside the container (for example via
|
|
| 63 |
|
| 64 |
## Common Optional Variables
|
| 65 |
|
| 66 |
-
|
| 67 |
-
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
## Quick Deployment
|
| 93 |
|
|
@@ -201,33 +232,64 @@ api.restart_space(repo_id=repo_id)
|
|
| 201 |
|
| 202 |
For this project, if you need stable dashboard access without cold starts, use paid hardware and set sleep time to `Never`.
|
| 203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
## Backup/Restore Flow
|
| 205 |
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
-
|
| 217 |
-
-
|
| 218 |
-
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
|
| 232 |
## Use sshx Inside the Container
|
| 233 |
|
|
|
|
| 24 |
- Store OpenClaw config/workspace under `/root/.openclaw`
|
| 25 |
- Restore state automatically from a Hugging Face Dataset on startup
|
| 26 |
- Run scheduled backups of OpenClaw data to a Hugging Face Dataset via `cron` (as `root` user)
|
| 27 |
+
- Incremental backup + dynamic strategy + AES-256-CBC encryption + large file splitting
|
| 28 |
+
- Backup watchdog (auto-triggers backup when cron fails)
|
| 29 |
+
- SSH service with auto-healing watchdog + host key generation
|
| 30 |
+
- CCMR (Claude Code Model Router) with 10 platform API key support
|
| 31 |
+
- Multi-dataset restore (restore from a different dataset)
|
| 32 |
- Preinstall `python3`, `uv`, `vim`, `neovim`, `chromium` (via Chrome for Testing archive), `gh`, `hf`, `opencode`, `codex`, `claude` (Claude Code CLI), `@larksuite/cli` (with `npx skills add larksuite/cli -y -g`), and `sshx` in the image for interactive terminal use
|
| 33 |
|
| 34 |
## Repository Layout
|
| 35 |
|
| 36 |
- `Dockerfile`: Runtime image for the Space
|
| 37 |
- `scripts/openclaw-entrypoint.sh`: Main startup flow (restore, config generation, cron setup, gateway start)
|
| 38 |
+
- `scripts/hf-entrypoint.sh`: HF Spaces container entrypoint (PID 1, manages supervisord + SSH + PM2 + BT Panel)
|
| 39 |
+
- `scripts/supervisord.conf`: Supervisord config, manages cron, backup-watchdog, openclaw-gateway, ccmr-gateway
|
| 40 |
+
- `openclaw_hf/backup.py`: Backup/restore implementation (full/incremental, encryption, split, dynamic strategy, resume)
|
| 41 |
- `scripts/openclaw-backup-cron.sh`: Cron entrypoint for backup jobs
|
| 42 |
+
- `scripts/openclaw-backup-watchdog.sh`: Backup watchdog, auto-triggers backup when overdue
|
| 43 |
+
- `scripts/openclaw-backup-health.sh`: Backup health check & auto-repair
|
| 44 |
- `scripts/openclaw-restore.sh`: Startup restore entrypoint
|
| 45 |
+
- `scripts/openclaw-gateway-ctl`: Gateway process management (start/stop/restart/reload)
|
| 46 |
+
- `scripts/openclaw-env-sync.sh`: Sync environment variables from HF API
|
| 47 |
+
- `scripts/update-env-from-secrets.sh`: Fetch latest env vars from HF API
|
| 48 |
+
- `scripts/bt_install_panel_custom.sh`: BT Panel installation script
|
| 49 |
- `scripts/bootstrap-hf.sh`: Interactive bootstrap for Space/Dataset creation, upload, and Space variables/secrets setup (macOS/Linux)
|
| 50 |
- `scripts/bootstrap-hf.ps1`: Interactive bootstrap for Space/Dataset creation, upload, and Space variables/secrets setup (Windows PowerShell)
|
| 51 |
+
- `scripts/rebuild-space.sh`: Force push latest code to Space and trigger rebuild
|
| 52 |
+
- `scripts/delete-backups.sh`: Batch cleanup old backups from Dataset
|
| 53 |
+
- `scripts/delete-hf.py`: HF resource deletion tool (Space/Dataset/files/storage)
|
| 54 |
+
- `scripts/find-largest-backup.py`: Find best backup in Dataset
|
| 55 |
+
- `scripts/ssh_service_watchdog.sh`: SSH service watchdog (process monitor + auto-recovery)
|
| 56 |
+
- `scripts/check_ssh_health.sh`: SSH health check (used by Docker HEALTHCHECK)
|
| 57 |
+
- `scripts/ssh-agent-autostart.sh`: SSH agent auto-start and key loading
|
| 58 |
+
- `scripts/optimize_ssh.sh`: SSH configuration optimization
|
| 59 |
+
- `scripts/save-env.sh`: Save environment to `/etc/profile.d`
|
| 60 |
+
- `scripts/hf-storage.sh` / `scripts/hf-storage.py`: HuggingFace storage utilities
|
| 61 |
+
- `scripts/ccmr-setup.sh`: CCMR configuration generation
|
| 62 |
+
- `scripts/ccmr-wrapper.sh`: CCMR Supervisor wrapper (hot-reload + crash recovery)
|
| 63 |
+
- `scripts/server.js`: PID 1 keep-alive HTTP server
|
| 64 |
+
- `pm2/ecosystem.config.js`: PM2 configuration (optional extension)
|
| 65 |
- `tests/test_backup.py`: Unit tests for the backup module
|
| 66 |
- `tests/test_entrypoint_config.py`: Unit tests for gateway config generation behavior
|
| 67 |
|
|
|
|
| 89 |
|
| 90 |
## Common Optional Variables
|
| 91 |
|
| 92 |
+
| Variable | Default | Description |
|
| 93 |
+
|----------|---------|-------------|
|
| 94 |
+
| `OPENCLAW_VERSION` | `latest` | OpenClaw version for Docker install |
|
| 95 |
+
| `OPENCLAW_GATEWAY_PORT` | `18789` | Gateway listen port |
|
| 96 |
+
| `OPENCLAW_GATEWAY_BIND` | `lan` | Gateway bind mode (`lan`/`local`) |
|
| 97 |
+
| `OPENCLAW_STATE_DIR` | `/root/.openclaw` | OpenClaw state directory |
|
| 98 |
+
| `OPENCLAW_USER` | `root` | Runtime user for gateway and cron |
|
| 99 |
+
| `OPENCLAW_GROUP` | `root` | Runtime group |
|
| 100 |
+
| `OPENCLAW_CONFIG_PATH` | `/root/.openclaw/openclaw.json` | Gateway config path |
|
| 101 |
+
| `OPENCLAW_WORKSPACE_DIR` | `/root/.openclaw/workspace` | Workspace directory |
|
| 102 |
+
| `OPENCLAW_BACKUP_CRON` | `*/10 * * * *` | Backup cron expression |
|
| 103 |
+
| `OPENCLAW_BACKUP_SOURCE_DIR` | `/root/.openclaw` | Backup/restore base directory |
|
| 104 |
+
| `OPENCLAW_BACKUP_ROOT_*_DIR` | Various | Extra backup dirs (config, codex, claude, agents, ssh, env, npm, lark-cli) |
|
| 105 |
+
| `OPENCLAW_BACKUP_PATH_PREFIX` | `backups` | Backup path prefix |
|
| 106 |
+
| `OPENCLAW_BACKUP_KEEP_COUNT` | `24` | Number of backups to keep |
|
| 107 |
+
| `OPENCLAW_BACKUP_ENCRYPTION_ENABLED` | `false` | Enable AES-256-CBC encryption |
|
| 108 |
+
| `OPENCLAW_BACKUP_SPLIT_SIZE` | `500M` | Large file split volume size |
|
| 109 |
+
| `OPENCLAW_INCREMENTAL_BACKUP` | `true` | Enable incremental backup |
|
| 110 |
+
| `OPENCLAW_DYNAMIC_BACKUP` | `true` | Enable dynamic backup strategy |
|
| 111 |
+
| `OPENCLAW_FULL_BACKUP_INTERVAL_HOURS` | `1` | Force full backup interval |
|
| 112 |
+
| `OPENCLAW_MAX_INCREMENTAL_BACKUPS` | `15` | Max incremental backups before full |
|
| 113 |
+
| `OPENCLAW_RESTORE_TIMEOUT` | `5400` | Restore timeout (seconds, 90 min) |
|
| 114 |
+
| `WATCHDOG_INTERVAL` | `600` | Backup watchdog check interval (s) |
|
| 115 |
+
| `MAX_BACKUP_AGE_MINUTES` | `30` | Max backup age (minutes) |
|
| 116 |
+
| `FORCE_BACKUP_INTERVAL` | `14400` | Force backup interval (seconds) |
|
| 117 |
+
| `OPENCLAW_SSHX_AUTO_START` | `false` | Auto-start `sshx` on boot |
|
| 118 |
+
| `OPENCLAW_GATEWAY_AUTH_MODE` | `token` | Auth mode (`token`/`password`) |
|
| 119 |
+
| `ROOT_PASSWORD` | `lauer3912` | SSH root password |
|
| 120 |
+
| `CCMR_ENABLED` | `false` | Enable Claude Code Model Router |
|
| 121 |
+
| `CCMR_PORT` | `8080` | CCMR gateway port |
|
| 122 |
|
| 123 |
## Quick Deployment
|
| 124 |
|
|
|
|
| 232 |
|
| 233 |
For this project, if you need stable dashboard access without cold starts, use paid hardware and set sleep time to `Never`.
|
| 234 |
|
| 235 |
+
## SSH Service
|
| 236 |
+
|
| 237 |
+
The container has a comprehensive SSH service guarding system to ensure continuous availability:
|
| 238 |
+
|
| 239 |
+
- **Auto-start**: Entrypoint generates host keys, cleans stale PID files, starts sshd
|
| 240 |
+
- **SSH Watchdog** (`ssh_service_watchdog.sh`): Monitors sshd every 30s, auto-recovers on failure
|
| 241 |
+
- **Multi-level repair**: Config corruption → backup config → minimal config → auto-reinstall openssh-server
|
| 242 |
+
- **Exponential backoff**: Gradually increases wait time on consecutive failures
|
| 243 |
+
- **Health check** (`check_ssh_health.sh`): Used by Docker HEALTHCHECK
|
| 244 |
+
- **SSH Agent auto-load**: Auto-starts ssh-agent and loads keys from `/root/.ssh/`
|
| 245 |
+
- **Root password**: Set via `ROOT_PASSWORD` environment variable
|
| 246 |
+
|
| 247 |
+
## CCMR (Claude Code Model Router)
|
| 248 |
+
|
| 249 |
+
CCMR gateway is integrated and managed by Supervisord with hot-reload support:
|
| 250 |
+
|
| 251 |
+
- **Auto-config**: Set `CCMR_*_API_KEY` env vars to enable
|
| 252 |
+
- **10 API Key slots**: DeepSeek, Qwen, Kimi, GLM, MiniMax (CN/Global), MiMo (SGP/CN/AMS/PAYG)
|
| 253 |
+
- **File hot-reload**: Edit `/root/.env.d/ccmr.env` and changes apply immediately without restart
|
| 254 |
+
- **Crash recovery**: Supervisord auto-restarts CCMR process
|
| 255 |
+
|
| 256 |
## Backup/Restore Flow
|
| 257 |
|
| 258 |
+
### Restore
|
| 259 |
+
|
| 260 |
+
**Automatic restore on startup** (always runs on container restart/rebuild):
|
| 261 |
+
|
| 262 |
+
- `openclaw-state` -> `OPENCLAW_BACKUP_SOURCE_DIR` (default `/root/.openclaw`)
|
| 263 |
+
- `root-config` -> `OPENCLAW_BACKUP_ROOT_CONFIG_DIR` (default `/root/.config`)
|
| 264 |
+
- `root-codex` -> `OPENCLAW_BACKUP_ROOT_CODEX_DIR` (default `/root/.codex`)
|
| 265 |
+
- `root-claude` -> `OPENCLAW_BACKUP_ROOT_CLAUDE_DIR` (default `/root/.claude`)
|
| 266 |
+
- `root-agents` -> `OPENCLAW_BACKUP_ROOT_AGENTS_DIR` (default `/root/.agents`)
|
| 267 |
+
- `root-ssh` -> `OPENCLAW_BACKUP_ROOT_SSH_DIR` (default `/root/.ssh`)
|
| 268 |
+
- `root-env` -> `OPENCLAW_BACKUP_ROOT_ENV_DIR` (default `/root/.env.d`)
|
| 269 |
+
- `root-npm` -> `OPENCLAW_BACKUP_ROOT_NPM_DIR` (default `/root/.npm`)
|
| 270 |
+
- `root-lark-cli` -> `OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR` (default `/root/.lark-cli`)
|
| 271 |
+
|
| 272 |
+
Multi-dataset restore: set `OPENCLAW_RESTORE_DATASET_REPO` to restore from a different dataset.
|
| 273 |
+
|
| 274 |
+
### Backup
|
| 275 |
+
|
| 276 |
+
- **Scheduled backup**: Runs based on `OPENCLAW_BACKUP_CRON` (default every 10 min)
|
| 277 |
+
- **Incremental backup** (default on): Only backs up changed files after a full backup
|
| 278 |
+
- **Dynamic strategy** (default on): Auto-adjusts compression and splitting based on file size and change rate
|
| 279 |
+
- **AES-256-CBC encryption**: Optional, allows secure storage on public datasets
|
| 280 |
+
- **Large file splitting**: Default 500MB per volume, avoids upload failures
|
| 281 |
+
- **Resume support**: Creates checkpoint files during upload, allows resume on interruption
|
| 282 |
+
- **Shutdown backup**: Final backup before container exit on stop signal
|
| 283 |
+
- **Retention**: Keeps newest `OPENCLAW_BACKUP_KEEP_COUNT` (default 24) archives, auto-deletes older ones
|
| 284 |
+
|
| 285 |
+
### Backup Watchdog
|
| 286 |
+
|
| 287 |
+
`openclaw-backup-watchdog.sh` acts as the **last line of defense**:
|
| 288 |
+
|
| 289 |
+
- Auto-triggers backup when no backup for `MAX_BACKUP_AGE_MINUTES` (default 30 min)
|
| 290 |
+
- Force backup every `FORCE_BACKUP_INTERVAL` (default 4 hours)
|
| 291 |
+
- File lock prevents concurrent execution
|
| 292 |
+
- Automatic backoff on consecutive failures
|
| 293 |
|
| 294 |
## Use sshx Inside the Container
|
| 295 |
|
README_zh.md
CHANGED
|
@@ -26,6 +26,11 @@ pinned: false
|
|
| 26 |
- OpenClaw 配置和工作目录存储在 `/root/.openclaw`
|
| 27 |
- 启动时自动从 Hugging Face Dataset 恢复状态
|
| 28 |
- 通过 `cron` 定时备份 OpenClaw 数据到 Hugging Face Dataset
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
- 预装工具:`python3`、`uv`、`vim`、`neovim`、`chromium`(Chrome for Testing)、`gh`、`hf`、`opencode`、`codex`、`claude`(Claude Code CLI)、`@larksuite/cli`、`sshx`
|
| 30 |
- 内置 DDNS-GO,支持动态 DNS 更新(非关键功能,安装失败不影响构建)
|
| 31 |
|
|
@@ -33,15 +38,33 @@ pinned: false
|
|
| 33 |
|
| 34 |
- `Dockerfile`: Space 运行时镜像
|
| 35 |
- `scripts/openclaw-entrypoint.sh`: 主启动流程(恢复、配置生成、cron 设置、gateway 启动)
|
| 36 |
-
- `scripts/hf-entrypoint.sh`: HF Spaces 容器入口(管理 supervisord + PM2)
|
| 37 |
-
- `scripts/supervisord.conf`: Supervisord 配置,管理 cron
|
| 38 |
-
- `openclaw_hf/backup.py`: 备份/恢复实现
|
| 39 |
- `scripts/openclaw-backup-cron.sh`: Cron 备份任务入口
|
|
|
|
|
|
|
| 40 |
- `scripts/openclaw-restore.sh`: 启动恢复入口
|
| 41 |
-
- `scripts/openclaw-gateway-
|
| 42 |
-
- `scripts/
|
|
|
|
|
|
|
| 43 |
- `scripts/bootstrap-hf.sh`: 交互式引导脚本(macOS/Linux)
|
| 44 |
- `scripts/bootstrap-hf.ps1`: 交互式引导脚本(Windows PowerShell)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
- `tests/test_backup.py`: 备份模块单元测试
|
| 46 |
- `tests/test_entrypoint_config.py`: Gateway 配置生成行为单元测试
|
| 47 |
|
|
@@ -60,7 +83,30 @@ pinned: false
|
|
| 60 |
|
| 61 |
- `BT_PANEL_PORT`(默认: `7860`): BT Panel 监听端口
|
| 62 |
- `BT_PANEL_USERNAME`(默认: `btadmin`): BT Panel 用户名,**必须以小写字母开头,只能包含小写字母和数字,长度 3-32 位**
|
| 63 |
-
- `BT_PANEL_PASSWORD`: BT Panel 密码,留空则
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
## 可选 LLM 变量(全有或全无)
|
| 66 |
|
|
@@ -84,15 +130,28 @@ pinned: false
|
|
| 84 |
| `OPENCLAW_GROUP` | `root` | 运行时用户组 |
|
| 85 |
| `OPENCLAW_CONFIG_PATH` | `/root/.openclaw/openclaw.json` | Gateway 配置文件路径 |
|
| 86 |
| `OPENCLAW_WORKSPACE_DIR` | `/root/.openclaw/workspace` | 工作目录 |
|
| 87 |
-
| `OPENCLAW_BACKUP_CRON` | `*/
|
| 88 |
| `OPENCLAW_BACKUP_SOURCE_DIR` | `/root/.openclaw` | 备份/恢复基础目录 |
|
| 89 |
| `OPENCLAW_BACKUP_ROOT_*_DIR` | 各有默认值 | 额外备份目录(config、codex、claude、agents、ssh、npm、lark-cli) |
|
| 90 |
| `OPENCLAW_BACKUP_PATH_PREFIX` | `backups` | 备份路径前缀 |
|
| 91 |
| `OPENCLAW_BACKUP_KEEP_COUNT` | `24` | 保留的最新备份数量 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
| `OPENCLAW_SSHX_AUTO_START` | `false` | 设为 `true` 启动时自动后台运行 `sshx` |
|
| 93 |
| `OPENCLAW_GATEWAY_AUTH_MODE` | `token` | 认证模式(`token`/`password`) |
|
| 94 |
| `OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH` | `false` | 允许不安全认证 |
|
| 95 |
| `OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH` | `false` | 禁用设备配对(**强烈不建议在公网开启**) |
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
## 快速部署
|
| 98 |
|
|
@@ -145,21 +204,42 @@ Space URL 构成:
|
|
| 145 |
|
| 146 |
## 备份/恢复流程
|
| 147 |
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
- **关机备份**:容器收到停止信号时,执行最后一次备份后再退出
|
| 161 |
- **保留策略**:上传后只保留最新的 `OPENCLAW_BACKUP_KEEP_COUNT`(默认 24 个)带时间戳的备份归档,自动删除更旧的
|
| 162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
## 在容器内使用 sshx
|
| 164 |
|
| 165 |
`sshx` 已预装在镜像中。
|
|
@@ -200,21 +280,35 @@ python3 -m unittest discover -s tests -p 'test_*.py'
|
|
| 200 |
|
| 201 |
## 架构说明
|
| 202 |
|
| 203 |
-
容器启动顺序:
|
| 204 |
|
| 205 |
```
|
| 206 |
-
PID 1 (
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
```
|
| 212 |
|
| 213 |
-
进程管理使用 **Supervisord**
|
| 214 |
- Supervisord 是为"进程管理者"设计的,适合 PID 1 下运行
|
| 215 |
- 信号传递正确,SIGTERM 能正确传递给子进程
|
| 216 |
- 相比 PM2 更轻量,职责单一
|
| 217 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
## License
|
| 219 |
|
| 220 |
MIT. See `LICENSE`.
|
|
|
|
| 26 |
- OpenClaw 配置和工作目录存储在 `/root/.openclaw`
|
| 27 |
- 启动时自动从 Hugging Face Dataset 恢复状态
|
| 28 |
- 通过 `cron` 定时备份 OpenClaw 数据到 Hugging Face Dataset
|
| 29 |
+
- 增量备份 + 动态备份策略 + AES-256-CBC 加密 + 大文件分卷
|
| 30 |
+
- 备份看门狗(兜底保障,cron 失效时自动触发备份)
|
| 31 |
+
- SSH 服务自动守护(看门狗监控自愈 + 自动生成主机密钥)
|
| 32 |
+
- CCMR(Claude Code Model Router)支持,自动配置 10 个平台 API Key
|
| 33 |
+
- 多数据集恢复支持(可从不同 Dataset 恢复)
|
| 34 |
- 预装工具:`python3`、`uv`、`vim`、`neovim`、`chromium`(Chrome for Testing)、`gh`、`hf`、`opencode`、`codex`、`claude`(Claude Code CLI)、`@larksuite/cli`、`sshx`
|
| 35 |
- 内置 DDNS-GO,支持动态 DNS 更新(非关键功能,安装失败不影响构建)
|
| 36 |
|
|
|
|
| 38 |
|
| 39 |
- `Dockerfile`: Space 运行时镜像
|
| 40 |
- `scripts/openclaw-entrypoint.sh`: 主启动流程(恢复、配置生成、cron 设置、gateway 启动)
|
| 41 |
+
- `scripts/hf-entrypoint.sh`: HF Spaces 容器入口(PID 1,管理 supervisord + SSH + PM2 + BT Panel)
|
| 42 |
+
- `scripts/supervisord.conf`: Supervisord 配置,管理 cron、backup-watchdog、openclaw-gateway、ccmr-gateway
|
| 43 |
+
- `openclaw_hf/backup.py`: 备份/恢复实现(全量/增量、加密、分卷、动态策略、断点续传)
|
| 44 |
- `scripts/openclaw-backup-cron.sh`: Cron 备份任务入口
|
| 45 |
+
- `scripts/openclaw-backup-watchdog.sh`: 备份看门狗,兜底保障自动触发备份
|
| 46 |
+
- `scripts/openclaw-backup-health.sh`: 备份健康检查与自动修复
|
| 47 |
- `scripts/openclaw-restore.sh`: 启动恢复入口
|
| 48 |
+
- `scripts/openclaw-gateway-ctl`: Gateway 进程管理(start/stop/restart/reload)
|
| 49 |
+
- `scripts/openclaw-env-sync.sh`: 从 HF API 同步环境变量
|
| 50 |
+
- `scripts/update-env-from-secrets.sh`: 从 HF API 获取最新环境变量
|
| 51 |
+
- `scripts/bt_install_panel_custom.sh`: BT Panel 安装脚本
|
| 52 |
- `scripts/bootstrap-hf.sh`: 交互式引导脚本(macOS/Linux)
|
| 53 |
- `scripts/bootstrap-hf.ps1`: 交互式引导脚本(Windows PowerShell)
|
| 54 |
+
- `scripts/rebuild-space.sh`: 强制推送最新代码到 Space 并触发重建
|
| 55 |
+
- `scripts/delete-backups.sh`: 批量清理 Dataset 中的旧备份
|
| 56 |
+
- `scripts/delete-hf.py`: HF 资源删除工具(Space/Dataset/文件/存储)
|
| 57 |
+
- `scripts/find-largest-backup.py`: 查找 Dataset 中最佳备份
|
| 58 |
+
- `scripts/ssh_service_watchdog.sh`: SSH 服务看门狗(进程监控 + 自动恢复)
|
| 59 |
+
- `scripts/check_ssh_health.sh`: SSH 健康检查脚本(Docker HEALTHCHECK 使用)
|
| 60 |
+
- `scripts/ssh-agent-autostart.sh`: SSH Agent 自动启动和加载密钥
|
| 61 |
+
- `scripts/optimize_ssh.sh`: SSH 配置优化
|
| 62 |
+
- `scripts/save-env.sh`: 保存环境变量到 `/etc/profile.d`
|
| 63 |
+
- `scripts/hf-storage.sh` / `scripts/hf-storage.py`: HuggingFace 公共存储工具
|
| 64 |
+
- `scripts/ccmr-setup.sh`: CCMR 配置生成脚本
|
| 65 |
+
- `scripts/ccmr-wrapper.sh`: CCMR Supervisor 启动包装(文件热加载 + 崩溃恢复)
|
| 66 |
+
- `scripts/server.js`: PID 1 存活 HTTP 服务
|
| 67 |
+
- `pm2/ecosystem.config.js`: PM2 配置文件(可选扩展)
|
| 68 |
- `tests/test_backup.py`: 备份模块单元测试
|
| 69 |
- `tests/test_entrypoint_config.py`: Gateway 配置生成行为单元测试
|
| 70 |
|
|
|
|
| 83 |
|
| 84 |
- `BT_PANEL_PORT`(默认: `7860`): BT Panel 监听端口
|
| 85 |
- `BT_PANEL_USERNAME`(默认: `btadmin`): BT Panel 用户名,**必须以小写字母开头,只能包含小写字母和数字,长度 3-32 位**
|
| 86 |
+
- `BT_PANEL_PASSWORD`: BT Panel 密码,留空则自动生成
|
| 87 |
+
- `BT_PANEL_SAFE_PATH`: BT Panel 安全入口路径(默认: `lauer3912`)
|
| 88 |
+
- `BT_PANEL_TIMEZONE`(默认: `Asia/Shanghai`): BT Panel 时区
|
| 89 |
+
|
| 90 |
+
## SSH 服务
|
| 91 |
+
|
| 92 |
+
容器内置 SSH 服务守护体系,确保 SSH 持续可用:
|
| 93 |
+
|
| 94 |
+
- **自动启动**: 入口点自动生成主机密钥、清理残留 PID、启动 sshd
|
| 95 |
+
- **SSH 看门狗** (`ssh_service_watchdog.sh`): 每 30 秒监控 sshd 进程,异常时自动恢复
|
| 96 |
+
- **多级修复**: 配置损坏→使用备份配置→生成最小配置→自动重装 openssh-server
|
| 97 |
+
- **指数退避**: 连续失败时逐步增加等待时间,避免频繁重试
|
| 98 |
+
- **健康检查** (`check_ssh_health.sh`): 配合 Docker HEALTHCHECK 使用
|
| 99 |
+
- **SSH Agent 自动加载**: 自动启动 ssh-agent 并加载 `/root/.ssh/` 下的私钥
|
| 100 |
+
- **ROOT 密码**: 通过 `ROOT_PASSWORD` 环境变量设置
|
| 101 |
+
|
| 102 |
+
## CCMR(Claude Code Model Router)
|
| 103 |
+
|
| 104 |
+
集成 CCMR 网关,通过 Supervisor 管理,支持热加载:
|
| 105 |
+
|
| 106 |
+
- **自动配置**: 设置 `CCMR_*_API_KEY` 环境变量即可启用
|
| 107 |
+
- **10 个 API Key 支持**: DeepSeek、通义千问、Kimi、智谱 GLM、MiniMax(CN/Global)、MiMo(SGP/CN/AMS/PAYG)
|
| 108 |
+
- **文件热加载**: 编辑 `/root/.env.d/ccmr.env` 后立即生效,无需重启
|
| 109 |
+
- **崩溃恢复**: Supervisor 自动重启 CCMR 进程
|
| 110 |
|
| 111 |
## 可选 LLM 变量(全有或全无)
|
| 112 |
|
|
|
|
| 130 |
| `OPENCLAW_GROUP` | `root` | 运行时用户组 |
|
| 131 |
| `OPENCLAW_CONFIG_PATH` | `/root/.openclaw/openclaw.json` | Gateway 配置文件路径 |
|
| 132 |
| `OPENCLAW_WORKSPACE_DIR` | `/root/.openclaw/workspace` | 工作目录 |
|
| 133 |
+
| `OPENCLAW_BACKUP_CRON` | `*/10 * * * *` | 备份 cron 表达式(默认每 10 分钟) |
|
| 134 |
| `OPENCLAW_BACKUP_SOURCE_DIR` | `/root/.openclaw` | 备份/恢复基础目录 |
|
| 135 |
| `OPENCLAW_BACKUP_ROOT_*_DIR` | 各有默认值 | 额外备份目录(config、codex、claude、agents、ssh、npm、lark-cli) |
|
| 136 |
| `OPENCLAW_BACKUP_PATH_PREFIX` | `backups` | 备份路径前缀 |
|
| 137 |
| `OPENCLAW_BACKUP_KEEP_COUNT` | `24` | 保留的最新备份数量 |
|
| 138 |
+
| `OPENCLAW_BACKUP_ENCRYPTION_ENABLED` | `false` | 启用 AES-256-CBC 加密备份 |
|
| 139 |
+
| `OPENCLAW_BACKUP_SPLIT_SIZE` | `500M` | 大文件分卷大小 |
|
| 140 |
+
| `OPENCLAW_INCREMENTAL_BACKUP` | `true` | 启用增量备份 |
|
| 141 |
+
| `OPENCLAW_DYNAMIC_BACKUP` | `true` | 启用动态备份策略 |
|
| 142 |
+
| `OPENCLAW_FULL_BACKUP_INTERVAL_HOURS` | `1` | 强制全量备份间隔 |
|
| 143 |
+
| `OPENCLAW_MAX_INCREMENTAL_BACKUPS` | `15` | 增量备份次数上限 |
|
| 144 |
+
| `OPENCLAW_RESTORE_TIMEOUT` | `5400` | 恢复超时(秒,默认 90 分钟) |
|
| 145 |
+
| `WATCHDOG_INTERVAL` | `600` | 备份看门狗检查间隔(秒) |
|
| 146 |
+
| `MAX_BACKUP_AGE_MINUTES` | `30` | 备份最大间隔(分钟) |
|
| 147 |
+
| `FORCE_BACKUP_INTERVAL` | `14400` | 强制备份间隔(秒) |
|
| 148 |
| `OPENCLAW_SSHX_AUTO_START` | `false` | 设为 `true` 启动时自动后台运行 `sshx` |
|
| 149 |
| `OPENCLAW_GATEWAY_AUTH_MODE` | `token` | 认证模式(`token`/`password`) |
|
| 150 |
| `OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH` | `false` | 允许不安全认证 |
|
| 151 |
| `OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH` | `false` | 禁用设备配对(**强烈不建议在公网开启**) |
|
| 152 |
+
| `ROOT_PASSWORD` | `lauer3912` | SSH root 用户密码 |
|
| 153 |
+
| `CCMR_ENABLED` | `false` | 启用 Claude Code Model Router |
|
| 154 |
+
| `CCMR_PORT` | `8080` | CCMR 网关端口 |
|
| 155 |
|
| 156 |
## 快速部署
|
| 157 |
|
|
|
|
| 204 |
|
| 205 |
## 备份/恢复流程
|
| 206 |
|
| 207 |
+
### 恢复流程
|
| 208 |
+
|
| 209 |
+
**启动时从 Dataset 自动恢复**,容器重启或重建时必定执行:
|
| 210 |
+
|
| 211 |
+
- `openclaw-state` -> `OPENCLAW_BACKUP_SOURCE_DIR`(默认 `/root/.openclaw`)
|
| 212 |
+
- `root-config` -> `OPENCLAW_BACKUP_ROOT_CONFIG_DIR`(默认 `/root/.config`)
|
| 213 |
+
- `root-codex` -> `OPENCLAW_BACKUP_ROOT_CODEX_DIR`(默认 `/root/.codex`)
|
| 214 |
+
- `root-claude` -> `OPENCLAW_BACKUP_ROOT_CLAUDE_DIR`(默认 `/root/.claude`)
|
| 215 |
+
- `root-agents` -> `OPENCLAW_BACKUP_ROOT_AGENTS_DIR`(默认 `/root/.agents`)
|
| 216 |
+
- `root-ssh` -> `OPENCLAW_BACKUP_ROOT_SSH_DIR`(默认 `/root/.ssh`)
|
| 217 |
+
- `root-env` -> `OPENCLAW_BACKUP_ROOT_ENV_DIR`(默认 `/root/.env.d`)
|
| 218 |
+
- `root-npm` -> `OPENCLAW_BACKUP_ROOT_NPM_DIR`(默认 `/root/.npm`)
|
| 219 |
+
- `root-lark-cli` -> `OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR`(默认 `/root/.lark-cli`)
|
| 220 |
+
|
| 221 |
+
支持多数据集恢复:设置 `OPENCLAW_RESTORE_DATASET_REPO` 可以从不同 Dataset 恢复。
|
| 222 |
+
|
| 223 |
+
### 备份流程
|
| 224 |
+
|
| 225 |
+
- **定时备份**:根据 `OPENCLAW_BACKUP_CRON`(默认每 10 分钟)执行
|
| 226 |
+
- **增量备份**(默认开启):全量备份后只备份变化文件,大幅减少数据量
|
| 227 |
+
- **动态备份策略**(默认开启):根据文件大小和变化率自动调整压缩级别和分卷策略
|
| 228 |
+
- **AES-256-CBC 加密**:可选开启,加密后的备份可安全存储在公开 Dataset
|
| 229 |
+
- **大文件分卷**:默认 500MB 分卷,避免单文件过大导致上传失败
|
| 230 |
+
- **断点续传**:上传过程中创建 checkpoint 文件,中断后可恢复
|
| 231 |
- **关机备份**:容器收到停止信号时,执行最后一次备份后再退出
|
| 232 |
- **保留策略**:上传后只保留最新的 `OPENCLAW_BACKUP_KEEP_COUNT`(默认 24 个)带时间戳的备份归档,自动删除更旧的
|
| 233 |
|
| 234 |
+
### 备份看门狗
|
| 235 |
+
|
| 236 |
+
`openclaw-backup-watchdog.sh` 作为备份系统的**兜底保障**:
|
| 237 |
+
|
| 238 |
+
- 即使 cron 失效,看门狗也会在超过 `MAX_BACKUP_AGE_MINUTES`(默认 30 分钟)无备份时自动触发
|
| 239 |
+
- 每 `FORCE_BACKUP_INTERVAL`(默认 4 小时)强制执行一次备份
|
| 240 |
+
- 使用文件锁防止并发执行
|
| 241 |
+
- 连续失败时自动退避
|
| 242 |
+
|
| 243 |
## 在容器内使用 sshx
|
| 244 |
|
| 245 |
`sshx` 已预装在镜像中。
|
|
|
|
| 280 |
|
| 281 |
## 架构说明
|
| 282 |
|
| 283 |
+
容器启动顺序(`hf-entrypoint.sh` 作为 PID 1):
|
| 284 |
|
| 285 |
```
|
| 286 |
+
PID 1 (node hf-server.js)
|
| 287 |
+
├── supervisord (后台)
|
| 288 |
+
│ ├── cron
|
| 289 |
+
│ ├── backup-watchdog
|
| 290 |
+
│ ├── openclaw-gateway (via openclaw-entrypoint.sh)
|
| 291 |
+
│ │ └── openclaw (gateway 进程管理器)
|
| 292 |
+
│ └── ccmr-gateway (via ccmr-wrapper.sh, 启用时)
|
| 293 |
+
├── sshd
|
| 294 |
+
├── ssh_service_watchdog.sh (SSH 看门狗)
|
| 295 |
+
├── PM2 (可选,ecosystem.config.js 有配置时)
|
| 296 |
+
└── BT Panel
|
| 297 |
```
|
| 298 |
|
| 299 |
+
进程管理使用 **Supervisord** 管理后台服务,原因:
|
| 300 |
- Supervisord 是为"进程管理者"设计的,适合 PID 1 下运行
|
| 301 |
- 信号传递正确,SIGTERM 能正确传递给子进程
|
| 302 |
- 相比 PM2 更轻量,职责单一
|
| 303 |
|
| 304 |
+
关键启动步骤:
|
| 305 |
+
1. `supervisord` 后台启动,管理 cron/backup-watchdog/openclaw-gateway/ccmr-gateway
|
| 306 |
+
2. 生成 SSH 主机密钥,启动 sshd + SSH 看门狗
|
| 307 |
+
3. 等待 openclaw-gateway 完成备份恢复
|
| 308 |
+
4. 可选启动 PM2 管理附加进程
|
| 309 |
+
5. 启动 BT Panel
|
| 310 |
+
6. `node hf-server.js` 作为 PID 1 前台进程接管容器生命周期
|
| 311 |
+
|
| 312 |
## License
|
| 313 |
|
| 314 |
MIT. See `LICENSE`.
|
scripts/hf-entrypoint.sh
CHANGED
|
@@ -67,7 +67,7 @@ fi
|
|
| 67 |
|
| 68 |
# 1. 确保SSH服务启动
|
| 69 |
if ! pgrep -x "sshd" > /dev/null 2>&1; then
|
| 70 |
-
|
| 71 |
if [ -x "/usr/sbin/sshd" ]; then
|
| 72 |
_sshd_bin="/usr/sbin/sshd"
|
| 73 |
elif [ -x "/usr/bin/sshd" ]; then
|
|
|
|
| 67 |
|
| 68 |
# 1. 确保SSH服务启动
|
| 69 |
if ! pgrep -x "sshd" > /dev/null 2>&1; then
|
| 70 |
+
_sshd_bin=""
|
| 71 |
if [ -x "/usr/sbin/sshd" ]; then
|
| 72 |
_sshd_bin="/usr/sbin/sshd"
|
| 73 |
elif [ -x "/usr/bin/sshd" ]; then
|
scripts/rebuild-space.sh
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# ============================================================
|
| 3 |
+
# OpenClaw HF Space 使用最新代码重建脚本
|
| 4 |
+
#
|
| 5 |
+
# 用法:
|
| 6 |
+
# ./rebuild-space.sh <SPACE_REPO_ID> [HF_TOKEN]
|
| 7 |
+
#
|
| 8 |
+
# 示例:
|
| 9 |
+
# ./rebuild-space.sh GGSheng/page hf_xxxxx
|
| 10 |
+
#
|
| 11 |
+
# 方式1:直接提供参数
|
| 12 |
+
# ./scripts/rebuild-space.sh GGSheng/page hf_xxxxx
|
| 13 |
+
#
|
| 14 |
+
# 方式2:设置环境变量
|
| 15 |
+
# export SPACE_REPO_ID="GGSheng/page"
|
| 16 |
+
# export HF_TOKEN="hf_xxxxx"
|
| 17 |
+
# ./scripts/rebuild-space.sh
|
| 18 |
+
#
|
| 19 |
+
# 方式3:使用缓存的 token(默认从 ~/.cache/huggingface/token 读取)
|
| 20 |
+
# ./scripts/rebuild-space.sh GGSheng/page
|
| 21 |
+
#
|
| 22 |
+
# 环境变量 (可选):
|
| 23 |
+
# SPACE_REPO_ID - Space 仓库 ID (如 GGSheng/page)
|
| 24 |
+
# HF_TOKEN - Hugging Face API Token
|
| 25 |
+
# HF_TOKEN_FILE - Token 文件路径 (默认 ~/.cache/huggingface/token)
|
| 26 |
+
#
|
| 27 |
+
# 说明:
|
| 28 |
+
# 此脚本用于将本地最新代码强制推送到 Hugging Face Space 并触发重建
|
| 29 |
+
# - 自动创建 Space(如果不存在)
|
| 30 |
+
# - 覆盖 Space 中的所有文件
|
| 31 |
+
# - 排除 .git, logs, scripts, docs, tests 等非必要文件
|
| 32 |
+
# - 推送完成后自动请求 Space 重启以使更改生效
|
| 33 |
+
#
|
| 34 |
+
# 注意事项:
|
| 35 |
+
# 1. 确保 HF_TOKEN 有 write 权限
|
| 36 |
+
# 2. Space 必须使用 Docker SDK
|
| 37 |
+
# 3. 推送后 Space 会自动重启(需等待构建完成)
|
| 38 |
+
# 4. 此操作会覆盖 Space 中的现有文件
|
| 39 |
+
# ============================================================
|
| 40 |
+
|
| 41 |
+
update_readme_title() {
|
| 42 |
+
local new_title="$1"
|
| 43 |
+
local readme_file="$REPO_ROOT/README.md"
|
| 44 |
+
local temp_file
|
| 45 |
+
|
| 46 |
+
if [[ ! -f "$readme_file" ]]; then
|
| 47 |
+
return 0
|
| 48 |
+
fi
|
| 49 |
+
|
| 50 |
+
temp_file="$(mktemp)"
|
| 51 |
+
if sed "s/^title:.*/title: $new_title/" "$readme_file" > "$temp_file"; then
|
| 52 |
+
mv "$temp_file" "$readme_file"
|
| 53 |
+
else
|
| 54 |
+
rm -f "$temp_file"
|
| 55 |
+
fi
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
restore_readme_title() {
|
| 59 |
+
local original_title="$1"
|
| 60 |
+
local readme_file="$REPO_ROOT/README.md"
|
| 61 |
+
local temp_file
|
| 62 |
+
|
| 63 |
+
if [[ ! -f "$readme_file" ]]; then
|
| 64 |
+
return 0
|
| 65 |
+
fi
|
| 66 |
+
|
| 67 |
+
temp_file="$(mktemp)"
|
| 68 |
+
if sed "s/^title:.*/title: $original_title/" "$readme_file" > "$temp_file"; then
|
| 69 |
+
mv "$temp_file" "$readme_file"
|
| 70 |
+
else
|
| 71 |
+
rm -f "$temp_file"
|
| 72 |
+
fi
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
SPACE_REPO_ID="${1:-${SPACE_REPO_ID:-}}"
|
| 76 |
+
HF_TOKEN="${2:-${HF_TOKEN:-}}"
|
| 77 |
+
|
| 78 |
+
if [[ -z "$SPACE_REPO_ID" ]]; then
|
| 79 |
+
echo "Usage: $0 <SPACE_REPO_ID> [HF_TOKEN]"
|
| 80 |
+
echo ""
|
| 81 |
+
echo "Examples:"
|
| 82 |
+
echo " $0 GGSheng/page hf_xxxxx"
|
| 83 |
+
echo " SPACE_REPO_ID=GGSheng/page HF_TOKEN=hf_xxxxx $0"
|
| 84 |
+
echo " $0 GGSheng/page"
|
| 85 |
+
echo ""
|
| 86 |
+
echo "Environment variables:"
|
| 87 |
+
echo " SPACE_REPO_ID - Space repo ID (e.g. GGSheng/page)"
|
| 88 |
+
echo " HF_TOKEN - Hugging Face API Token"
|
| 89 |
+
echo " HF_TOKEN_FILE - Token file path (default: ~/.cache/huggingface/token)"
|
| 90 |
+
exit 1
|
| 91 |
+
fi
|
| 92 |
+
|
| 93 |
+
if [[ -z "$HF_TOKEN" ]]; then
|
| 94 |
+
HF_TOKEN_FILE="${HF_TOKEN_FILE:-$HOME/.cache/huggingface/token}"
|
| 95 |
+
if [[ -f "$HF_TOKEN_FILE" ]]; then
|
| 96 |
+
HF_TOKEN="$(cat "$HF_TOKEN_FILE")"
|
| 97 |
+
fi
|
| 98 |
+
fi
|
| 99 |
+
|
| 100 |
+
if [[ -z "$HF_TOKEN" ]]; then
|
| 101 |
+
echo "Error: HF_TOKEN is required. Provide as 2nd arg, set HF_TOKEN env var, or ensure ~/.cache/huggingface/token exists."
|
| 102 |
+
exit 1
|
| 103 |
+
fi
|
| 104 |
+
|
| 105 |
+
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
| 106 |
+
REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
| 107 |
+
|
| 108 |
+
SPACE_NAME="${SPACE_REPO_ID##*/}"
|
| 109 |
+
|
| 110 |
+
echo "============================================"
|
| 111 |
+
echo "OpenClaw HF Space Rebuild Script"
|
| 112 |
+
echo "============================================"
|
| 113 |
+
echo "Space: $SPACE_REPO_ID"
|
| 114 |
+
echo "Repo: $REPO_ROOT"
|
| 115 |
+
echo ""
|
| 116 |
+
|
| 117 |
+
cd "$REPO_ROOT"
|
| 118 |
+
|
| 119 |
+
echo "[1/5] Logging into Hugging Face..."
|
| 120 |
+
echo "$HF_TOKEN" | hf auth login --token "$HF_TOKEN"
|
| 121 |
+
|
| 122 |
+
echo ""
|
| 123 |
+
echo "[2/5] Saving original README title..."
|
| 124 |
+
original_readme_title="$(sed -n 's/^title: *//p' "$REPO_ROOT/README.md" | head -n 1)"
|
| 125 |
+
update_readme_title "$SPACE_NAME"
|
| 126 |
+
|
| 127 |
+
restore_readme_on_exit() {
|
| 128 |
+
restore_readme_title "${original_readme_title:-HF Space}"
|
| 129 |
+
}
|
| 130 |
+
trap restore_readme_on_exit EXIT
|
| 131 |
+
|
| 132 |
+
echo ""
|
| 133 |
+
echo "[3/5] Ensuring Space exists..."
|
| 134 |
+
hf repos create "$SPACE_REPO_ID" --repo-type space --space-sdk docker --exist-ok
|
| 135 |
+
|
| 136 |
+
echo ""
|
| 137 |
+
echo "[4/5] Uploading files to Space..."
|
| 138 |
+
upload_output=$(hf upload "$SPACE_REPO_ID" . --repo-type space \
|
| 139 |
+
--exclude '.git/**' \
|
| 140 |
+
--exclude '.git' \
|
| 141 |
+
--exclude '.github/**' \
|
| 142 |
+
--exclude 'logs/**' \
|
| 143 |
+
--exclude '*.log' \
|
| 144 |
+
--exclude 'docs/**' \
|
| 145 |
+
--exclude 'tests/**' \
|
| 146 |
+
--exclude 'LICENSE' \
|
| 147 |
+
--exclude '.dockerignore' \
|
| 148 |
+
--exclude '.python-version' \
|
| 149 |
+
--commit-message "fix: improve SSH service stability and backup.py error handling" 2>&1)
|
| 150 |
+
upload_exit_code=$?
|
| 151 |
+
|
| 152 |
+
if [ $upload_exit_code -ne 0 ]; then
|
| 153 |
+
echo "Error: Upload failed:"
|
| 154 |
+
echo "$upload_output"
|
| 155 |
+
exit 1
|
| 156 |
+
fi
|
| 157 |
+
|
| 158 |
+
echo ""
|
| 159 |
+
echo "[5/5] Requesting Space restart..."
|
| 160 |
+
restart_output=$(hf spaces restart "$SPACE_REPO_ID" 2>&1)
|
| 161 |
+
restart_exit_code=$?
|
| 162 |
+
|
| 163 |
+
if [ $restart_exit_code -ne 0 ]; then
|
| 164 |
+
echo "Warning: Restart request failed:"
|
| 165 |
+
echo "$restart_output"
|
| 166 |
+
fi
|
| 167 |
+
|
| 168 |
+
echo ""
|
| 169 |
+
echo "============================================"
|
| 170 |
+
echo "Done! Space updated: https://huggingface.co/spaces/$SPACE_REPO_ID"
|
| 171 |
+
echo "============================================"
|