GGSheng commited on
Commit
e178b46
·
verified ·
1 Parent(s): 125a077

fix: improve SSH service stability and backup.py error handling

Browse files
Files changed (4) hide show
  1. README.md +114 -52
  2. README_zh.md +120 -26
  3. scripts/hf-entrypoint.sh +1 -1
  4. 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
- - `openclaw_hf/backup.py`: Backup/restore implementation
 
 
34
  - `scripts/openclaw-backup-cron.sh`: Cron entrypoint for backup jobs
 
 
35
  - `scripts/openclaw-restore.sh`: Startup restore entrypoint
36
- - `scripts/openclaw-gateway-restart`: Kill running `openclaw-gateway` processes
 
 
 
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
- - `OPENCLAW_LLM_MODEL` (unset by default; used only when custom model preconfiguration is enabled)
67
- - `OPENCLAW_LLM_PROVIDER` (default: `thirdparty`)
68
- - `OPENCLAW_LLM_API` (default: `openai-completions`)
69
- - `OPENCLAW_VERSION` (used by Docker install step; bootstrap prompts for it and defaults to the latest version detected from npm registry, fallback `latest`)
70
- - `OPENCLAW_STATE_DIR` (default: `/root/.openclaw`)
71
- - `OPENCLAW_USER` (default: `root`, runtime user for gateway and cron jobs)
72
- - `OPENCLAW_GROUP` (default: `root`, runtime group for gateway and cron jobs)
73
- - `OPENCLAW_CONFIG_PATH` (default: `/root/.openclaw/openclaw.json`)
74
- - `OPENCLAW_WORKSPACE_DIR` (default: `/root/.openclaw/workspace`)
75
- - `OPENCLAW_BACKUP_CRON` (default: `*/30 * * * *`, backup every 30 minutes)
76
- - `OPENCLAW_BACKUP_SOURCE_DIR` (default: `/root/.openclaw`, backup/restore base directory for `openclaw-state`)
77
- - `OPENCLAW_BACKUP_ROOT_CONFIG_DIR` (default: `/root/.config`, additional backup/restore directory for `root-config`)
78
- - `OPENCLAW_BACKUP_ROOT_CODEX_DIR` (default: `/root/.codex`, additional backup/restore directory for `root-codex`)
79
- - `OPENCLAW_BACKUP_ROOT_CLAUDE_DIR` (default: `/root/.claude`, additional backup/restore directory for `root-claude`)
80
- - `OPENCLAW_BACKUP_ROOT_AGENTS_DIR` (default: `/root/.agents`, additional backup/restore directory for `root-agents`)
81
- - `OPENCLAW_BACKUP_ROOT_SSH_DIR` (default: `/root/.ssh`, additional backup/restore directory for `root-ssh`)
82
- - `OPENCLAW_BACKUP_ROOT_ENV_DIR` (default: `/root/.env.d`, additional backup/restore directory for `root-env`)
83
- - `OPENCLAW_BACKUP_ROOT_NPM_DIR` (default: `/root/.npm`, additional backup/restore directory for `root-npm`)
84
- - `OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR` (default: `/root/.lark-cli`, additional backup/restore directory for `root-lark-cli`)
85
- - `OPENCLAW_BACKUP_PATH_PREFIX` (default: `backups`)
86
- - `OPENCLAW_BACKUP_KEEP_COUNT` (default: `24`, keep newest N timestamped backup archives; older ones are auto-deleted)
87
- - `OPENCLAW_SSHX_AUTO_START` (default: `false`; set `true` to auto-run `sshx` in background on startup)
88
- - `OPENCLAW_GATEWAY_AUTH_MODE` (default: `token`, optional: `password`)
89
- - `OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH` (default: `false`)
90
- - `OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH` (default: `false`; set `true` to bypass pairing, strongly discouraged on public networks)
 
 
 
 
 
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
- - Startup restore: on container startup, it fetches `latest-backup.json` from the Dataset to locate the latest backup and restore:
207
- - `openclaw-state` -> `OPENCLAW_BACKUP_SOURCE_DIR` (default `/root/.openclaw`)
208
- - `root-config` -> `OPENCLAW_BACKUP_ROOT_CONFIG_DIR` (default `/root/.config`, restored only when present in archive)
209
- - `root-codex` -> `OPENCLAW_BACKUP_ROOT_CODEX_DIR` (default `/root/.codex`, restored only when present in archive)
210
- - `root-claude` -> `OPENCLAW_BACKUP_ROOT_CLAUDE_DIR` (default `/root/.claude`, restored only when present in archive)
211
- - `root-agents` -> `OPENCLAW_BACKUP_ROOT_AGENTS_DIR` (default `/root/.agents`, restored only when present in archive)
212
- - `root-ssh` -> `OPENCLAW_BACKUP_ROOT_SSH_DIR` (default `/root/.ssh`, restored only when present in archive)
213
- - `root-env` -> `OPENCLAW_BACKUP_ROOT_ENV_DIR` (default `/root/.env.d`, restored only when present in archive)
214
- - `root-npm` -> `OPENCLAW_BACKUP_ROOT_NPM_DIR` (default `/root/.npm`, restored only when present in archive)
215
- - `root-lark-cli` -> `OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR` (default `/root/.lark-cli`, restored only when present in archive)
216
- - Scheduled backup: cron runs based on `OPENCLAW_BACKUP_CRON`
217
- - Shutdown backup: when the container receives a stop signal, one final backup is uploaded before exit
218
- - Each backup upload includes:
219
- - `backups/openclaw-backup-<timestamp>.tar.gz`
220
- - archive root `openclaw-state/`
221
- - archive root `root-config/` (included when `OPENCLAW_BACKUP_ROOT_CONFIG_DIR` exists)
222
- - archive root `root-codex/` (included when `OPENCLAW_BACKUP_ROOT_CODEX_DIR` exists)
223
- - archive root `root-claude/` (included when `OPENCLAW_BACKUP_ROOT_CLAUDE_DIR` exists)
224
- - archive root `root-agents/` (included when `OPENCLAW_BACKUP_ROOT_AGENTS_DIR` exists)
225
- - archive root `root-ssh/` (included when `OPENCLAW_BACKUP_ROOT_SSH_DIR` exists)
226
- - archive root `root-env/` (included when `OPENCLAW_BACKUP_ROOT_ENV_DIR` exists)
227
- - archive root `root-npm/` (included when `OPENCLAW_BACKUP_ROOT_NPM_DIR` exists)
228
- - archive root `root-lark-cli/` (included when `OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR` exists)
229
- - `latest-backup.json`
230
- - Retention: after upload, only the newest `OPENCLAW_BACKUP_KEEP_COUNT` timestamped archives are kept (default `24`); older timestamped archives are deleted automatically
 
 
 
 
 
 
 
 
 
 
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 配置,管理 cronopenclaw-gateway
38
- - `openclaw_hf/backup.py`: 备份/恢复实现
39
  - `scripts/openclaw-backup-cron.sh`: Cron 备份任务入口
 
 
40
  - `scripts/openclaw-restore.sh`: 启动恢复入口
41
- - `scripts/openclaw-gateway-restart`: 重启 openclaw-gateway 进程
42
- - `scripts/bt_install_panel.sh`: BT Panel 安装脚本
 
 
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` | `*/30 * * * *` | 备份 cron 表达式(默认每 30 分钟) |
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
- - **启动恢复**:容器启动时从 Dataset 获取 `latest-backup.json` 来定位最新备份并恢复:
149
- - `openclaw-state` -> `OPENCLAW_BACKUP_SOURCE_DIR`(默认 `/root/.openclaw`)
150
- - `root-config` -> `OPENCLAW_BACKUP_ROOT_CONFIG_DIR`(默认 `/root/.config`)
151
- - `root-codex` -> `OPENCLAW_BACKUP_ROOT_CODEX_DIR`(默认 `/root/.codex`)
152
- - `root-claude` -> `OPENCLAW_BACKUP_ROOT_CLAUDE_DIR`(默认 `/root/.claude`)
153
- - `root-agents` -> `OPENCLAW_BACKUP_ROOT_AGENTS_DIR`(默认 `/root/.agents`)
154
- - `root-ssh` -> `OPENCLAW_BACKUP_ROOT_SSH_DIR`(默认 `/root/.ssh`)
155
- - `root-env` -> `OPENCLAW_BACKUP_ROOT_ENV_DIR`(默认 `/root/.env.d`)
156
- - `root-npm` -> `OPENCLAW_BACKUP_ROOT_NPM_DIR`(默认 `/root/.npm`)
157
- - `root-lark-cli` -> `OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR`(默认 `/root/.lark-cli`)
158
-
159
- - **定时备份**:根据 `OPENCLAW_BACKUP_CRON` 执行
 
 
 
 
 
 
 
 
 
 
 
 
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 (bt-panel)
207
- ── supervisord
208
- ├── cron
209
- ── openclaw-gateway (via openclaw-entrypoint.sh)
210
- ── node hf-server.js (PID 1 替代)
 
 
 
 
 
 
211
  ```
212
 
213
- 进程管理使用 **Supervisord** 而非 PM2,原因:
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
- local _sshd_bin=""
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 "============================================"