Commit ·
9584c6b
0
Parent(s):
Initial commit
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .claude/scheduled_tasks.lock +1 -0
- .env.example +194 -0
- .gitattributes +39 -0
- .gitignore +18 -0
- Dockerfile +73 -0
- HuggingClaw-social.jpg +3 -0
- HuggingClaw-social.png +3 -0
- HuggingClaw.png +3 -0
- README.md +14 -0
- abc.txt +0 -0
- app.py +8 -0
- assets/architecture.svg +31 -0
- assets/home-preview.png +3 -0
- assets/office-preview.png +3 -0
- config_for_dataset.json +54 -0
- extensions/coding-agent/index.ts +269 -0
- extensions/coding-agent/openclaw.plugin.json +38 -0
- extensions/coding-agent/package.json +11 -0
- frontend/btn-back-home-sprite.png +3 -0
- frontend/btn-broker-sprite.png +3 -0
- frontend/btn-diy-sprite.png +3 -0
- frontend/btn-move-house-sprite.png +3 -0
- frontend/btn-open-drawer-sprite.png +3 -0
- frontend/btn-state-sprite.png +3 -0
- frontend/cats-spritesheet.webp +3 -0
- frontend/coffee-machine-shadow-v1.png +3 -0
- frontend/coffee-machine-v3-grid.webp +3 -0
- frontend/desk-v3.webp +3 -0
- frontend/electron-standalone.html +0 -0
- frontend/error-bug-spritesheet-grid.webp +3 -0
- frontend/flowers-bloom-v2.webp +3 -0
- frontend/fonts/OFL.txt +94 -0
- frontend/fonts/ark-pixel-12px-proportional-ja.ttf.woff2 +3 -0
- frontend/fonts/ark-pixel-12px-proportional-ko.ttf.woff2 +3 -0
- frontend/fonts/ark-pixel-12px-proportional-latin.ttf.woff2 +3 -0
- frontend/fonts/ark-pixel-12px-proportional-zh_cn.ttf.woff2 +3 -0
- frontend/fonts/ark-pixel-12px-proportional-zh_hk.ttf.woff2 +3 -0
- frontend/fonts/ark-pixel-12px-proportional-zh_tr.ttf.woff2 +3 -0
- frontend/fonts/ark-pixel-12px-proportional-zh_tw.ttf.woff2 +3 -0
- frontend/fonts/ark-pixel-font-12px-proportional-ttf.woff2-v2025.10.20.zip +3 -0
- frontend/guest_anim_1.webp +3 -0
- frontend/guest_anim_2.webp +3 -0
- frontend/guest_anim_3.webp +3 -0
- frontend/guest_anim_4.webp +3 -0
- frontend/guest_anim_5.webp +3 -0
- frontend/guest_anim_6.webp +3 -0
- frontend/guest_role_1.png +3 -0
- frontend/guest_role_2.png +3 -0
- frontend/guest_role_3.png +3 -0
- frontend/guest_role_4.png +3 -0
.claude/scheduled_tasks.lock
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"sessionId":"9cf03742-3f47-4a01-bbe9-989dff2c6421","pid":64066,"acquiredAt":1773282355683}
|
.env.example
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 2 |
+
# HuggingClaw — Environment Configuration
|
| 3 |
+
# Deploy OpenClaw on HuggingFace Spaces
|
| 4 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 5 |
+
#
|
| 6 |
+
# Usage:
|
| 7 |
+
# Local Docker → cp .env.example .env → fill in values → docker run --env-file .env
|
| 8 |
+
# HF Spaces → Set each variable as a "Repository Secret" in Space Settings
|
| 9 |
+
#
|
| 10 |
+
# Legend:
|
| 11 |
+
# [REQUIRED] Must be set, or data persistence will not work
|
| 12 |
+
# [RECOMMENDED] Strongly recommended for production use
|
| 13 |
+
# [OPTIONAL] Fine-tune behavior; safe to leave empty
|
| 14 |
+
#
|
| 15 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# ─── SECURITY ─────────────────────────────────────────────────────────────
|
| 19 |
+
#
|
| 20 |
+
# Password for the Control UI dashboard.
|
| 21 |
+
# Visitors can see the UI, but only users with this password can connect
|
| 22 |
+
# and control the OpenClaw instance (manage agents, plugins, settings).
|
| 23 |
+
#
|
| 24 |
+
# [RECOMMENDED] Default: huggingclaw
|
| 25 |
+
#
|
| 26 |
+
OPENCLAW_PASSWORD=huggingclaw
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# ─── DATA PERSISTENCE ────────────────────────────────────────────────────
|
| 30 |
+
#
|
| 31 |
+
# HuggingClaw auto-syncs the ~/.openclaw directory to a private HF Dataset
|
| 32 |
+
# repo, so your conversations, settings, and credentials survive restarts.
|
| 33 |
+
# Without these two variables, all data is lost when the Space restarts.
|
| 34 |
+
#
|
| 35 |
+
# HuggingFace Access Token with WRITE permission.
|
| 36 |
+
# Create one at: https://huggingface.co/settings/tokens
|
| 37 |
+
# Scopes needed: read + write access to your repos.
|
| 38 |
+
#
|
| 39 |
+
# [REQUIRED]
|
| 40 |
+
#
|
| 41 |
+
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 42 |
+
|
| 43 |
+
# Target Dataset repository for data backup.
|
| 44 |
+
# Format: your-username/repo-name
|
| 45 |
+
# Example: your-name/YourSpace-data
|
| 46 |
+
#
|
| 47 |
+
# Manual mode (default): create the repo yourself, then set this variable.
|
| 48 |
+
# Auto mode (AUTO_CREATE_DATASET=true): if not set, HuggingClaw derives
|
| 49 |
+
# it from your HF_TOKEN username → "your-username/HuggingClaw-data".
|
| 50 |
+
#
|
| 51 |
+
# [REQUIRED in manual mode, OPTIONAL in auto mode]
|
| 52 |
+
#
|
| 53 |
+
OPENCLAW_DATASET_REPO=your-username/HuggingClaw-data
|
| 54 |
+
|
| 55 |
+
# Whether to auto-create the Dataset repo if it doesn't exist.
|
| 56 |
+
# When true: HuggingClaw creates a PRIVATE dataset repo on first startup.
|
| 57 |
+
# If OPENCLAW_DATASET_REPO is not set, the repo name is auto-derived
|
| 58 |
+
# from your HF_TOKEN username (e.g. "your-username/HuggingClaw-data").
|
| 59 |
+
# When false (default): you must create the repo manually on HuggingFace
|
| 60 |
+
# and set OPENCLAW_DATASET_REPO yourself.
|
| 61 |
+
#
|
| 62 |
+
# [OPTIONAL] Default: false
|
| 63 |
+
#
|
| 64 |
+
# AUTO_CREATE_DATASET=false
|
| 65 |
+
|
| 66 |
+
# How often (in seconds) to back up data to the Dataset repo.
|
| 67 |
+
# Lower values = safer but more API calls to HuggingFace.
|
| 68 |
+
#
|
| 69 |
+
# [OPTIONAL] Default: 60
|
| 70 |
+
#
|
| 71 |
+
# SYNC_INTERVAL=60
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# ─── LLM PROVIDERS ────────────────────────────────────────────────────────
|
| 75 |
+
#
|
| 76 |
+
# HuggingClaw passes ALL environment variables directly to OpenClaw.
|
| 77 |
+
# Any provider that OpenClaw supports works out of the box — just set the
|
| 78 |
+
# API key as a Secret in your HF Space settings.
|
| 79 |
+
#
|
| 80 |
+
# See OpenClaw docs: https://openclawdoc.com/docs/reference/environment-variables
|
| 81 |
+
#
|
| 82 |
+
# [RECOMMENDED] At least one of the following for AI conversations
|
| 83 |
+
#
|
| 84 |
+
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 85 |
+
# OPENAI_BASE_URL=https://api.openai.com/v1
|
| 86 |
+
# OPENROUTER_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 87 |
+
# NVIDIA_API_KEY=nvapi-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 88 |
+
# ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 89 |
+
# GOOGLE_API_KEY=AIza...
|
| 90 |
+
# MISTRAL_API_KEY=mis-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 91 |
+
# COHERE_API_KEY=co-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 92 |
+
|
| 93 |
+
# Default model for new conversations (must exist in your configured provider).
|
| 94 |
+
#
|
| 95 |
+
# [OPTIONAL] Examples: openai/gpt-5-nano, openrouter/deepseek/deepseek-chat:free
|
| 96 |
+
#
|
| 97 |
+
# OPENCLAW_DEFAULT_MODEL=openai/gpt-5-nano
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
# ─── PERFORMANCE ──────────────────────────────────────────────────────────
|
| 101 |
+
#
|
| 102 |
+
# Node.js heap memory limit in MB.
|
| 103 |
+
# HF free tier provides 16 GB RAM. Default 512 MB is enough for most cases.
|
| 104 |
+
# Increase if you run complex agent workflows or handle large conversations.
|
| 105 |
+
#
|
| 106 |
+
# [OPTIONAL] Default: 512
|
| 107 |
+
#
|
| 108 |
+
# NODE_MEMORY_LIMIT=512
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
# ─��─ LOCALE ───────────────────────────────────────────────────────────────
|
| 112 |
+
#
|
| 113 |
+
# Timezone for log timestamps and scheduled tasks.
|
| 114 |
+
#
|
| 115 |
+
# [OPTIONAL] Default: UTC
|
| 116 |
+
#
|
| 117 |
+
# TZ=Asia/Shanghai
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
# ─── OPENCLAW (from official docs) ─────────────────────────────────────────
|
| 121 |
+
#
|
| 122 |
+
# Optional overrides. See https://openclawdoc.com/docs/reference/environment-variables
|
| 123 |
+
#
|
| 124 |
+
# OPENCLAW_HOME=~/.openclaw
|
| 125 |
+
# OPENCLAW_STATE_DIR=~/.openclaw
|
| 126 |
+
# OPENCLAW_CONFIG_PATH=~/.openclaw/openclaw.json
|
| 127 |
+
# OPENCLAW_LOG_LEVEL=info
|
| 128 |
+
# OPENCLAW_API_PORT=8080
|
| 129 |
+
# OPENCLAW_WS_PORT=8081
|
| 130 |
+
# OPENCLAW_HOST=0.0.0.0
|
| 131 |
+
# OLLAMA_HOST=http://localhost:11434
|
| 132 |
+
# OPENCLAW_HTTP_PROXY=
|
| 133 |
+
# OPENCLAW_HTTPS_PROXY=
|
| 134 |
+
# OPENCLAW_NO_PROXY=
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 138 |
+
# OpenClaw 官方环境变量
|
| 139 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 140 |
+
#
|
| 141 |
+
# HuggingClaw 启动 OpenClaw 时透传整个环境(env=os.environ.copy()),
|
| 142 |
+
# 因此 OpenClaw 官方文档中列出的 **所有** 环境变量在 HF Spaces / Docker 中
|
| 143 |
+
# 设置后均可直接生效。
|
| 144 |
+
# 官方完整列表见:https://openclawdoc.com/docs/reference/environment-variables
|
| 145 |
+
#
|
| 146 |
+
# 常见类别(仅列举部分):
|
| 147 |
+
# API Keys: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY, MISTRAL_API_KEY,
|
| 148 |
+
# COHERE_API_KEY, OPENROUTER_API_KEY
|
| 149 |
+
# Server: OPENCLAW_API_PORT, OPENCLAW_WS_PORT, OPENCLAW_METRICS_PORT,
|
| 150 |
+
# OPENCLAW_HOST, OPENCLAW_TLS_*
|
| 151 |
+
# App: OPENCLAW_CONFIG, OPENCLAW_DATA_DIR, OPENCLAW_LOG_LEVEL,
|
| 152 |
+
# OPENCLAW_LOG_FORMAT, OPENCLAW_LOG_FILE, OPENCLAW_ENV
|
| 153 |
+
# Memory: OPENCLAW_MEMORY_BACKEND, OPENCLAW_REDIS_URL, OPENCLAW_SQLITE_PATH
|
| 154 |
+
# Network: OPENCLAW_HTTP_PROXY, OPENCLAW_HTTPS_PROXY, OPENCLAW_NO_PROXY,
|
| 155 |
+
# OPENCLAW_OUTBOUND_MODE
|
| 156 |
+
# Secrets: OPENCLAW_SECRETS_BACKEND, OPENCLAW_SECRETS_KEY, VAULT_ADDR, VAULT_TOKEN
|
| 157 |
+
# Ollama: OLLAMA_HOST, OLLAMA_NUM_PARALLEL, OLLAMA_KEEP_ALIVE
|
| 158 |
+
# Browser: OPENCLAW_BROWSER_EXECUTABLE, OPENCLAW_BROWSER_HEADLESS
|
| 159 |
+
#
|
| 160 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 161 |
+
#
|
| 162 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 163 |
+
# HuggingClaw 新增变量一览(仅本仓库脚本使用)
|
| 164 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
| 165 |
+
#
|
| 166 |
+
# ─── 安全 / 控制台 ───────────────────────────────────────────────────────
|
| 167 |
+
# OPENCLAW_PASSWORD [推荐] 控制台密码,未设则默认 huggingclaw
|
| 168 |
+
#
|
| 169 |
+
# ─── 持久化 (HuggingFace Dataset) ───────────────────────────────────────
|
| 170 |
+
# HF_TOKEN [必填] HF 访问令牌,需具备写入权限
|
| 171 |
+
# OPENCLAW_DATASET_REPO [必填] 备份用 Dataset 仓库,如 your-name/HuggingClaw-data
|
| 172 |
+
# AUTO_CREATE_DATASET [可选] 是否自动创建仓库,默认 false(安全考虑)
|
| 173 |
+
# SYNC_INTERVAL [可选] 备份间隔(秒),默认 60
|
| 174 |
+
# HF_HUB_DOWNLOAD_TIMEOUT [可选] 下载超时(秒),默认 300
|
| 175 |
+
# HF_HUB_UPLOAD_TIMEOUT [可选] 上传超时(秒),默认 600
|
| 176 |
+
#
|
| 177 |
+
# ─── LLM / 对话 API(至少配置其一以启用 AI 对话)────────────────────────
|
| 178 |
+
# 所有 OpenClaw 支持的 API Key 环境变量均可直接使用,无需额外配置。
|
| 179 |
+
# 完整列表见 OpenClaw 官方文档。常见:
|
| 180 |
+
# OPENAI_API_KEY, OPENROUTER_API_KEY, NVIDIA_API_KEY, ANTHROPIC_API_KEY,
|
| 181 |
+
# GOOGLE_API_KEY, MISTRAL_API_KEY, COHERE_API_KEY, OPENCLAW_DEFAULT_MODEL
|
| 182 |
+
#
|
| 183 |
+
# ─── 消息渠道 ─────────────────────────────────────────────────────────
|
| 184 |
+
# Telegram、WhatsApp 等消息渠道均可在 Control UI 中配置,无需环境变量。
|
| 185 |
+
#
|
| 186 |
+
# ─── HuggingFace Spaces 运行时(HF 自动注入,一般无需手动设)─��──────────
|
| 187 |
+
# SPACE_HOST 当前 Space 域名,如 xxx.hf.space
|
| 188 |
+
# SPACE_ID 仓库 ID,如 username/HuggingClaw
|
| 189 |
+
#
|
| 190 |
+
# ─── 性能与运行 ───────────────────────────────────────────────────────
|
| 191 |
+
# NODE_MEMORY_LIMIT [可选] Node 堆内存上限(MB),默认 512
|
| 192 |
+
# TZ [可选] 时区,如 Asia/Shanghai
|
| 193 |
+
#
|
| 194 |
+
# ═══════════════════════════════════════════════════════════════════════════
|
.gitattributes
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.webp filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 环境与密钥
|
| 2 |
+
.env
|
| 3 |
+
.env.local
|
| 4 |
+
*.pem
|
| 5 |
+
|
| 6 |
+
# 依赖与构建
|
| 7 |
+
node_modules/
|
| 8 |
+
|
| 9 |
+
# 日志与临时
|
| 10 |
+
*.log
|
| 11 |
+
.DS_Store
|
| 12 |
+
frontend/index.html
|
| 13 |
+
scripts/__pycache__/
|
| 14 |
+
.replit
|
| 15 |
+
replit.nix
|
| 16 |
+
.breakpoints
|
| 17 |
+
.config
|
| 18 |
+
upm.toml
|
Dockerfile
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenClaw on Hugging Face Spaces — Pre-built image (v4.1)
|
| 2 |
+
# Uses official pre-built image to avoid 30+ minute builds on cpu-basic
|
| 3 |
+
|
| 4 |
+
# ── Stage 1: Pull pre-built OpenClaw ─────────────────────────────────────────
|
| 5 |
+
FROM ghcr.io/openclaw/openclaw:latest AS openclaw-prebuilt
|
| 6 |
+
|
| 7 |
+
# ── Stage 2: Runtime ─────────────────────────────────────────────────────────
|
| 8 |
+
FROM node:22-bookworm
|
| 9 |
+
SHELL ["/bin/bash", "-c"]
|
| 10 |
+
|
| 11 |
+
# ── System dependencies (root) ───────────────────────────────────────────────
|
| 12 |
+
RUN echo "[build] Installing system deps..." && START=$(date +%s) \
|
| 13 |
+
&& apt-get update \
|
| 14 |
+
&& apt-get install -y --no-install-recommends git ca-certificates curl python3 python3-pip \
|
| 15 |
+
&& rm -rf /var/lib/apt/lists/* \
|
| 16 |
+
&& pip3 install --no-cache-dir --break-system-packages huggingface_hub requests \
|
| 17 |
+
&& corepack enable \
|
| 18 |
+
&& mkdir -p /app/openclaw \
|
| 19 |
+
&& chown -R node:node /app \
|
| 20 |
+
&& mkdir -p /home/node/.openclaw/workspace /home/node/.openclaw/credentials \
|
| 21 |
+
&& chown -R node:node /home/node \
|
| 22 |
+
&& echo "[build] System deps: $(($(date +%s) - START))s"
|
| 23 |
+
|
| 24 |
+
# ── Claude Code CLI + ACP (acpx) ──────────────────────────────────────────────
|
| 25 |
+
# Claude Code is the coding agent; acpx manages it via ACP (Agent Client Protocol)
|
| 26 |
+
RUN echo "[build] Installing Claude Code CLI + acpx..." && START=$(date +%s) \
|
| 27 |
+
&& npm install -g @anthropic-ai/claude-code acpx@latest \
|
| 28 |
+
&& echo "[build] Claude Code + acpx: $(($(date +%s) - START))s"
|
| 29 |
+
|
| 30 |
+
# ── Copy pre-built OpenClaw (skips clone + install + build entirely) ─────────
|
| 31 |
+
COPY --from=openclaw-prebuilt --chown=node:node /app /app/openclaw
|
| 32 |
+
|
| 33 |
+
USER node
|
| 34 |
+
ENV HOME=/home/node
|
| 35 |
+
WORKDIR /app
|
| 36 |
+
|
| 37 |
+
# ── A2A Gateway Extension ───────────────────────────────────────────────────
|
| 38 |
+
RUN echo "[build] Installing A2A gateway..." && START=$(date +%s) \
|
| 39 |
+
&& git clone --depth 1 https://github.com/win4r/openclaw-a2a-gateway.git /app/openclaw/extensions/a2a-gateway \
|
| 40 |
+
&& cd /app/openclaw/extensions/a2a-gateway \
|
| 41 |
+
&& npm install --production \
|
| 42 |
+
&& echo "[build] A2A gateway: $(($(date +%s) - START))s"
|
| 43 |
+
|
| 44 |
+
# ── Coding Agent Extension (local — no git clone needed) ─────────────────────
|
| 45 |
+
COPY --chown=node:node extensions/coding-agent /app/openclaw/extensions/coding-agent
|
| 46 |
+
|
| 47 |
+
# ── Pre-configure acpx for non-interactive ACP sessions ───────────────────
|
| 48 |
+
RUN mkdir -p /home/node/.acpx \
|
| 49 |
+
&& echo '{"defaultAgent":"claude","defaultPermissions":"approve-all","format":"text"}' \
|
| 50 |
+
> /home/node/.acpx/config.json
|
| 51 |
+
|
| 52 |
+
# ── Prepare runtime dirs ────────────────────────────────────────────────────
|
| 53 |
+
RUN mkdir -p /app/openclaw/empty-bundled-plugins \
|
| 54 |
+
&& node -e "try{console.log(require('/app/openclaw/package.json').version)}catch(e){console.log('unknown')}" > /app/openclaw/.version \
|
| 55 |
+
&& echo "[build] OpenClaw version: $(cat /app/openclaw/.version)"
|
| 56 |
+
|
| 57 |
+
# ── Scripts + Config + Frontend ──────────────────────────────────────────────
|
| 58 |
+
COPY --chown=node:node scripts /home/node/scripts
|
| 59 |
+
COPY --chown=node:node frontend /home/node/frontend
|
| 60 |
+
COPY --chown=node:node workspace-templates /home/node/workspace-templates
|
| 61 |
+
COPY --chown=node:node openclaw.json /home/node/scripts/openclaw.json.default
|
| 62 |
+
RUN chmod +x /home/node/scripts/entrypoint.sh /home/node/scripts/sync_hf.py \
|
| 63 |
+
&& VERSION_TS=$(date +%s) \
|
| 64 |
+
&& sed "s/{{VERSION_TIMESTAMP}}/${VERSION_TS}/g" /home/node/frontend/electron-standalone.html > /home/node/frontend/index.html \
|
| 65 |
+
&& echo "[build] Frontend index.html generated (timestamp=${VERSION_TS})"
|
| 66 |
+
|
| 67 |
+
ENV NODE_ENV=production
|
| 68 |
+
ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/openclaw/empty-bundled-plugins
|
| 69 |
+
ENV OPENCLAW_PREFER_PNPM=1
|
| 70 |
+
ENV PATH="/home/node/.local/bin:$PATH"
|
| 71 |
+
WORKDIR /home/node
|
| 72 |
+
|
| 73 |
+
CMD ["/home/node/scripts/entrypoint.sh"]
|
HuggingClaw-social.jpg
ADDED
|
Git LFS Details
|
HuggingClaw-social.png
ADDED
|
Git LFS Details
|
HuggingClaw.png
ADDED
|
Git LFS Details
|
README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: HuggingClaw Home
|
| 3 |
+
emoji: 🏠
|
| 4 |
+
colorFrom: yellow
|
| 5 |
+
colorTo: red
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
short_description: Family dashboard and orchestrator
|
| 10 |
+
app_port: 7860
|
| 11 |
+
tags:
|
| 12 |
+
- huggingface
|
| 13 |
+
- openclaw
|
| 14 |
+
---
|
abc.txt
ADDED
|
File without changes
|
app.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
if __name__ == "__main__":
|
| 5 |
+
# In a generic Docker Space, this might not be executed if CMD is set in Dockerfile.
|
| 6 |
+
# But if the user switches to generic Python SDK or wants to run it manually:
|
| 7 |
+
print("Starting OpenClaw Sync Wrapper...")
|
| 8 |
+
subprocess.run([sys.executable, "scripts/sync_hf.py"], check=True)
|
assets/architecture.svg
ADDED
|
|
assets/home-preview.png
ADDED
|
Git LFS Details
|
assets/office-preview.png
ADDED
|
Git LFS Details
|
config_for_dataset.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"gateway": {
|
| 3 |
+
"mode": "local",
|
| 4 |
+
"bind": "lan",
|
| 5 |
+
"port": 7860,
|
| 6 |
+
"auth": { "token": "openclaw-space-default" },
|
| 7 |
+
"controlUi": {
|
| 8 |
+
"allowInsecureAuth": true,
|
| 9 |
+
"allowedOrigins": [
|
| 10 |
+
"https://huggingface.co"
|
| 11 |
+
]
|
| 12 |
+
}
|
| 13 |
+
},
|
| 14 |
+
"session": { "scope": "global" },
|
| 15 |
+
"models": {
|
| 16 |
+
"mode": "merge",
|
| 17 |
+
"providers": {
|
| 18 |
+
"zhipu": {
|
| 19 |
+
"baseUrl": "https://open.bigmodel.cn/api/anthropic",
|
| 20 |
+
"apiKey": "<ENV_VAR>",
|
| 21 |
+
"api": "anthropic-messages",
|
| 22 |
+
"models": [
|
| 23 |
+
{ "id": "glm-4.5-air", "name": "GLM-4.5 Air" },
|
| 24 |
+
{ "id": "glm-4.5", "name": "GLM-4.5" },
|
| 25 |
+
{ "id": "glm-4.6", "name": "GLM-4.6" }
|
| 26 |
+
]
|
| 27 |
+
},
|
| 28 |
+
"hf": {
|
| 29 |
+
"baseUrl": "https://router.huggingface.co/v1",
|
| 30 |
+
"apiKey": "<ENV_VAR>",
|
| 31 |
+
"api": "openai-completions",
|
| 32 |
+
"models": [
|
| 33 |
+
{ "id": "Qwen/Qwen2.5-7B-Instruct", "name": "Qwen2.5 7B (HF Router)" }
|
| 34 |
+
]
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"plugins": {
|
| 39 |
+
"entries": {
|
| 40 |
+
"telegram": {
|
| 41 |
+
"enabled": true
|
| 42 |
+
},
|
| 43 |
+
"whatsapp": {
|
| 44 |
+
"enabled": true
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
},
|
| 48 |
+
"agents": {
|
| 49 |
+
"defaults": {
|
| 50 |
+
"workspace": "~/.openclaw/workspace",
|
| 51 |
+
"model": { "primary": "zhipu/glm-4.5-air" }
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
}
|
extensions/coding-agent/index.ts
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Coding Agent Extension for OpenClaw
|
| 3 |
+
*
|
| 4 |
+
* Integrates Claude Code via ACP (Agent Client Protocol) using acpx as the
|
| 5 |
+
* backend. All Claude Code invocations go through ACP for lifecycle management.
|
| 6 |
+
*
|
| 7 |
+
* Tools:
|
| 8 |
+
* - claude_code: Spawn Claude Code via ACP to autonomously complete coding tasks
|
| 9 |
+
* - hf_space_status: Check Space health/stage
|
| 10 |
+
* - hf_restart_space: Restart a Space
|
| 11 |
+
*/
|
| 12 |
+
|
| 13 |
+
import { execSync } from "node:child_process";
|
| 14 |
+
import { existsSync } from "node:fs";
|
| 15 |
+
|
| 16 |
+
// ── Types ────────────────────────────────────────────────────────────────────
|
| 17 |
+
|
| 18 |
+
interface PluginApi {
|
| 19 |
+
pluginConfig: Record<string, unknown>;
|
| 20 |
+
logger: { info: (...a: unknown[]) => void; warn: (...a: unknown[]) => void; error: (...a: unknown[]) => void };
|
| 21 |
+
registerTool?: (def: ToolDef) => void;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
interface ToolDef {
|
| 25 |
+
name: string;
|
| 26 |
+
description: string;
|
| 27 |
+
label?: string;
|
| 28 |
+
parameters: Record<string, unknown>;
|
| 29 |
+
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
interface ToolResult {
|
| 33 |
+
content: Array<{ type: "text"; text: string }>;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
| 37 |
+
|
| 38 |
+
const WORK_DIR = "/tmp/claude-workspace";
|
| 39 |
+
const CLAUDE_TIMEOUT = 300_000; // 5 minutes
|
| 40 |
+
|
| 41 |
+
function text(t: string): ToolResult {
|
| 42 |
+
return { content: [{ type: "text", text: t }] };
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
function asStr(v: unknown, fallback = ""): string {
|
| 46 |
+
return typeof v === "string" ? v : fallback;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
// ── Git helpers ──────────────────────────────────────────────────────────────
|
| 50 |
+
|
| 51 |
+
function ensureRepo(targetSpace: string, hfToken: string): void {
|
| 52 |
+
const repoUrl = `https://user:${hfToken}@huggingface.co/spaces/${targetSpace}`;
|
| 53 |
+
|
| 54 |
+
if (existsSync(`${WORK_DIR}/.git`)) {
|
| 55 |
+
// Reset to latest remote state (clean slate for each task)
|
| 56 |
+
try {
|
| 57 |
+
execSync("git fetch origin && git reset --hard origin/main", {
|
| 58 |
+
cwd: WORK_DIR,
|
| 59 |
+
timeout: 30_000,
|
| 60 |
+
stdio: "pipe",
|
| 61 |
+
});
|
| 62 |
+
return;
|
| 63 |
+
} catch {
|
| 64 |
+
// If fetch/reset fails, re-clone
|
| 65 |
+
execSync(`rm -rf ${WORK_DIR}`, { stdio: "pipe" });
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
// Fresh clone
|
| 70 |
+
if (existsSync(WORK_DIR)) {
|
| 71 |
+
execSync(`rm -rf ${WORK_DIR}`, { stdio: "pipe" });
|
| 72 |
+
}
|
| 73 |
+
execSync(`git clone --depth 20 ${repoUrl} ${WORK_DIR}`, {
|
| 74 |
+
timeout: 60_000,
|
| 75 |
+
stdio: "pipe",
|
| 76 |
+
});
|
| 77 |
+
execSync('git config user.name "Claude Code"', { cwd: WORK_DIR, stdio: "pipe" });
|
| 78 |
+
execSync('git config user.email "claude-code@huggingclaw"', { cwd: WORK_DIR, stdio: "pipe" });
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
function pushChanges(summary: string): string {
|
| 82 |
+
const status = execSync("git status --porcelain", {
|
| 83 |
+
cwd: WORK_DIR,
|
| 84 |
+
encoding: "utf-8",
|
| 85 |
+
}).trim();
|
| 86 |
+
|
| 87 |
+
if (!status) return "No files changed.";
|
| 88 |
+
|
| 89 |
+
execSync("git add -A", { cwd: WORK_DIR, stdio: "pipe" });
|
| 90 |
+
// Use a safe commit message
|
| 91 |
+
const msg = summary.slice(0, 72).replace(/"/g, '\\"');
|
| 92 |
+
execSync(`git commit -m "Claude Code: ${msg}"`, { cwd: WORK_DIR, stdio: "pipe" });
|
| 93 |
+
execSync("git push", { cwd: WORK_DIR, timeout: 60_000, stdio: "pipe" });
|
| 94 |
+
|
| 95 |
+
return `Pushed changes:\n${status}`;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// ── Plugin ───────────────────────────────────────────────────────────────────
|
| 99 |
+
|
| 100 |
+
const plugin = {
|
| 101 |
+
id: "coding-agent",
|
| 102 |
+
name: "Coding Agent",
|
| 103 |
+
description: "Claude Code via ACP for autonomous coding on HF Spaces",
|
| 104 |
+
|
| 105 |
+
register(api: PluginApi) {
|
| 106 |
+
const cfg = (api.pluginConfig as Record<string, unknown>) || {};
|
| 107 |
+
const targetSpace = asStr(cfg.targetSpace) || process.env.CODING_AGENT_TARGET_SPACE || "";
|
| 108 |
+
const hfToken = asStr(cfg.hfToken) || process.env.HF_TOKEN || "";
|
| 109 |
+
const zaiApiKey = asStr(cfg.zaiApiKey) || process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || "";
|
| 110 |
+
|
| 111 |
+
api.logger.info(`coding-agent: targetSpace=${targetSpace}, zaiKey=${zaiApiKey ? "set" : "missing"}`);
|
| 112 |
+
|
| 113 |
+
if (!api.registerTool) {
|
| 114 |
+
api.logger.warn("coding-agent: registerTool unavailable — no tools registered");
|
| 115 |
+
return;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
// ── Tool: claude_code ───────────────────────────────────────────────────
|
| 119 |
+
api.registerTool({
|
| 120 |
+
name: "claude_code",
|
| 121 |
+
label: "Run Claude Code",
|
| 122 |
+
description:
|
| 123 |
+
"Run Claude Code via ACP (acpx) to autonomously complete a coding task on the target HF Space. " +
|
| 124 |
+
"Claude Code clones the Space repo, analyzes code, makes changes, and pushes them back. " +
|
| 125 |
+
"Use for: debugging, fixing errors, adding features, refactoring.",
|
| 126 |
+
parameters: {
|
| 127 |
+
type: "object",
|
| 128 |
+
required: ["task"],
|
| 129 |
+
properties: {
|
| 130 |
+
task: {
|
| 131 |
+
type: "string",
|
| 132 |
+
description: "Detailed coding task description. Be specific about what to fix/change and why.",
|
| 133 |
+
},
|
| 134 |
+
auto_push: {
|
| 135 |
+
type: "boolean",
|
| 136 |
+
description: "Automatically push changes after Claude Code finishes (default: true)",
|
| 137 |
+
},
|
| 138 |
+
},
|
| 139 |
+
},
|
| 140 |
+
async execute(_id, params) {
|
| 141 |
+
const task = asStr(params.task);
|
| 142 |
+
const autoPush = params.auto_push !== false;
|
| 143 |
+
|
| 144 |
+
if (!targetSpace) return text("Error: no targetSpace configured");
|
| 145 |
+
if (!hfToken) return text("Error: no HF token configured");
|
| 146 |
+
if (!zaiApiKey) return text("Error: no ZAI_API_KEY or ZHIPU_API_KEY configured for Claude Code backend");
|
| 147 |
+
|
| 148 |
+
try {
|
| 149 |
+
// 1. Clone / reset to latest
|
| 150 |
+
api.logger.info(`coding-agent: Syncing repo ${targetSpace}...`);
|
| 151 |
+
ensureRepo(targetSpace, hfToken);
|
| 152 |
+
|
| 153 |
+
// 2. Run Claude Code via ACP (acpx)
|
| 154 |
+
api.logger.info(`coding-agent: Running Claude Code via ACP: ${task.slice(0, 100)}...`);
|
| 155 |
+
const claudeEnv: Record<string, string> = {
|
| 156 |
+
...(process.env as Record<string, string>),
|
| 157 |
+
ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic",
|
| 158 |
+
ANTHROPIC_AUTH_TOKEN: zaiApiKey,
|
| 159 |
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "GLM-4.7",
|
| 160 |
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "GLM-4.7",
|
| 161 |
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "GLM-4.5-Air",
|
| 162 |
+
CI: "true",
|
| 163 |
+
};
|
| 164 |
+
|
| 165 |
+
const output = execSync(
|
| 166 |
+
`acpx claude ${JSON.stringify(task)}`,
|
| 167 |
+
{
|
| 168 |
+
cwd: WORK_DIR,
|
| 169 |
+
env: claudeEnv,
|
| 170 |
+
timeout: CLAUDE_TIMEOUT,
|
| 171 |
+
encoding: "utf-8",
|
| 172 |
+
maxBuffer: 10 * 1024 * 1024, // 10MB
|
| 173 |
+
},
|
| 174 |
+
);
|
| 175 |
+
|
| 176 |
+
// 3. Push changes if requested
|
| 177 |
+
let pushResult = "Auto-push disabled.";
|
| 178 |
+
if (autoPush) {
|
| 179 |
+
try {
|
| 180 |
+
pushResult = pushChanges(task);
|
| 181 |
+
} catch (e: unknown) {
|
| 182 |
+
pushResult = `Push failed: ${e instanceof Error ? e.message : e}`;
|
| 183 |
+
}
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
return text(
|
| 187 |
+
`=== Claude Code Output ===\n${output}\n\n=== Changes ===\n${pushResult}`,
|
| 188 |
+
);
|
| 189 |
+
} catch (e: unknown) {
|
| 190 |
+
const msg = e instanceof Error ? e.message : String(e);
|
| 191 |
+
return text(`Claude Code failed:\n${msg.slice(0, 3000)}`);
|
| 192 |
+
}
|
| 193 |
+
},
|
| 194 |
+
});
|
| 195 |
+
|
| 196 |
+
// ── Tool: hf_space_status ───────────────────────────────────────────────
|
| 197 |
+
api.registerTool({
|
| 198 |
+
name: "hf_space_status",
|
| 199 |
+
label: "Check Space Health",
|
| 200 |
+
description:
|
| 201 |
+
"Check the current status of the target HuggingFace Space. " +
|
| 202 |
+
"Returns: stage (BUILDING, APP_STARTING, RUNNING, RUNTIME_ERROR, BUILD_ERROR, NO_APP_FILE).",
|
| 203 |
+
parameters: {
|
| 204 |
+
type: "object",
|
| 205 |
+
properties: {},
|
| 206 |
+
},
|
| 207 |
+
async execute() {
|
| 208 |
+
if (!targetSpace) return text("Error: no target space configured");
|
| 209 |
+
try {
|
| 210 |
+
const resp = await fetch(`https://huggingface.co/api/spaces/${targetSpace}`, {
|
| 211 |
+
headers: { Authorization: `Bearer ${hfToken}` },
|
| 212 |
+
});
|
| 213 |
+
if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText}`);
|
| 214 |
+
const data = (await resp.json()) as Record<string, unknown>;
|
| 215 |
+
const runtime = (data.runtime as Record<string, unknown>) || {};
|
| 216 |
+
const stage = runtime.stage || "unknown";
|
| 217 |
+
const hardware = runtime.hardware || "unknown";
|
| 218 |
+
|
| 219 |
+
// Try hitting the Space URL
|
| 220 |
+
let apiStatus = "not checked";
|
| 221 |
+
try {
|
| 222 |
+
const spaceUrl = `https://${targetSpace.replace("/", "-").toLowerCase()}.hf.space`;
|
| 223 |
+
const probe = await fetch(spaceUrl, { signal: AbortSignal.timeout(8000) });
|
| 224 |
+
apiStatus = probe.ok ? `reachable (${probe.status})` : `error (${probe.status})`;
|
| 225 |
+
} catch {
|
| 226 |
+
apiStatus = "unreachable";
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
return text(
|
| 230 |
+
`Space: ${targetSpace}\nStage: ${stage}\nHardware: ${hardware}\nAPI: ${apiStatus}`,
|
| 231 |
+
);
|
| 232 |
+
} catch (e: unknown) {
|
| 233 |
+
return text(`Error checking space: ${e instanceof Error ? e.message : e}`);
|
| 234 |
+
}
|
| 235 |
+
},
|
| 236 |
+
});
|
| 237 |
+
|
| 238 |
+
// ── Tool: hf_restart_space ──────────────────────────────────────────────
|
| 239 |
+
api.registerTool({
|
| 240 |
+
name: "hf_restart_space",
|
| 241 |
+
label: "Restart Space",
|
| 242 |
+
description: "Restart the target HuggingFace Space. Use when the Space is stuck or after deploying fixes.",
|
| 243 |
+
parameters: {
|
| 244 |
+
type: "object",
|
| 245 |
+
properties: {},
|
| 246 |
+
},
|
| 247 |
+
async execute() {
|
| 248 |
+
if (!targetSpace) return text("Error: no target space configured");
|
| 249 |
+
try {
|
| 250 |
+
const resp = await fetch(`https://huggingface.co/api/spaces/${targetSpace}/restart`, {
|
| 251 |
+
method: "POST",
|
| 252 |
+
headers: { Authorization: `Bearer ${hfToken}` },
|
| 253 |
+
});
|
| 254 |
+
if (!resp.ok) {
|
| 255 |
+
const body = await resp.text().catch(() => "");
|
| 256 |
+
throw new Error(`${resp.status}: ${body}`);
|
| 257 |
+
}
|
| 258 |
+
return text(`Space ${targetSpace} is restarting`);
|
| 259 |
+
} catch (e: unknown) {
|
| 260 |
+
return text(`Error restarting space: ${e instanceof Error ? e.message : e}`);
|
| 261 |
+
}
|
| 262 |
+
},
|
| 263 |
+
});
|
| 264 |
+
|
| 265 |
+
api.logger.info("coding-agent: Registered 3 tools (claude_code, hf_space_status, hf_restart_space)");
|
| 266 |
+
},
|
| 267 |
+
};
|
| 268 |
+
|
| 269 |
+
export default plugin;
|
extensions/coding-agent/openclaw.plugin.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"id": "coding-agent",
|
| 3 |
+
"name": "Coding Agent",
|
| 4 |
+
"description": "Claude Code sub-agent for autonomous coding on HF Spaces — powered by Zhipu GLM via z.ai",
|
| 5 |
+
"version": "2.0.0",
|
| 6 |
+
"defaultConfig": {
|
| 7 |
+
"targetSpace": "",
|
| 8 |
+
"targetDataset": "",
|
| 9 |
+
"hfToken": "",
|
| 10 |
+
"zaiApiKey": ""
|
| 11 |
+
},
|
| 12 |
+
"configSchema": {
|
| 13 |
+
"type": "object",
|
| 14 |
+
"additionalProperties": false,
|
| 15 |
+
"properties": {
|
| 16 |
+
"targetSpace": {
|
| 17 |
+
"type": "string",
|
| 18 |
+
"description": "HuggingFace Space repo ID to manage (e.g. tao-shen/HuggingClaw-Cain)"
|
| 19 |
+
},
|
| 20 |
+
"targetDataset": {
|
| 21 |
+
"type": "string",
|
| 22 |
+
"description": "HuggingFace Dataset repo ID for persistent data (e.g. tao-shen/HuggingClaw-Cain-data)"
|
| 23 |
+
},
|
| 24 |
+
"hfToken": {
|
| 25 |
+
"type": "string",
|
| 26 |
+
"description": "HuggingFace API token (or use HF_TOKEN env var)"
|
| 27 |
+
},
|
| 28 |
+
"zaiApiKey": {
|
| 29 |
+
"type": "string",
|
| 30 |
+
"description": "Z.AI API key for Claude Code backend (or use ZAI_API_KEY / ZHIPU_API_KEY env var)"
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
},
|
| 34 |
+
"uiHints": {
|
| 35 |
+
"hfToken": { "sensitive": true },
|
| 36 |
+
"zaiApiKey": { "sensitive": true }
|
| 37 |
+
}
|
| 38 |
+
}
|
extensions/coding-agent/package.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "openclaw-coding-agent",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"type": "module",
|
| 5 |
+
"description": "OpenClaw coding agent extension — HF Space management tools with syntax validation",
|
| 6 |
+
"main": "index.ts",
|
| 7 |
+
"openclaw": {
|
| 8 |
+
"extensions": ["./index.ts"]
|
| 9 |
+
},
|
| 10 |
+
"dependencies": {}
|
| 11 |
+
}
|
frontend/btn-back-home-sprite.png
ADDED
|
|
Git LFS Details
|
frontend/btn-broker-sprite.png
ADDED
|
|
Git LFS Details
|
frontend/btn-diy-sprite.png
ADDED
|
|
Git LFS Details
|
frontend/btn-move-house-sprite.png
ADDED
|
|
Git LFS Details
|
frontend/btn-open-drawer-sprite.png
ADDED
|
|
Git LFS Details
|
frontend/btn-state-sprite.png
ADDED
|
|
Git LFS Details
|
frontend/cats-spritesheet.webp
ADDED
|
|
Git LFS Details
|
frontend/coffee-machine-shadow-v1.png
ADDED
|
Git LFS Details
|
frontend/coffee-machine-v3-grid.webp
ADDED
|
Git LFS Details
|
frontend/desk-v3.webp
ADDED
|
Git LFS Details
|
frontend/electron-standalone.html
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
frontend/error-bug-spritesheet-grid.webp
ADDED
|
|
Git LFS Details
|
frontend/flowers-bloom-v2.webp
ADDED
|
Git LFS Details
|
frontend/fonts/OFL.txt
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2021, TakWolf (https://takwolf.com),
|
| 2 |
+
with Reserved Font Name "Ark Pixel".
|
| 3 |
+
|
| 4 |
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
| 5 |
+
This license is copied below, and is also available with a FAQ at:
|
| 6 |
+
https://openfontlicense.org
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
-----------------------------------------------------------
|
| 10 |
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
| 11 |
+
-----------------------------------------------------------
|
| 12 |
+
|
| 13 |
+
PREAMBLE
|
| 14 |
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
| 15 |
+
development of collaborative font projects, to support the font creation
|
| 16 |
+
efforts of academic and linguistic communities, and to provide a free and
|
| 17 |
+
open framework in which fonts may be shared and improved in partnership
|
| 18 |
+
with others.
|
| 19 |
+
|
| 20 |
+
The OFL allows the licensed fonts to be used, studied, modified and
|
| 21 |
+
redistributed freely as long as they are not sold by themselves. The
|
| 22 |
+
fonts, including any derivative works, can be bundled, embedded,
|
| 23 |
+
redistributed and/or sold with any software provided that any reserved
|
| 24 |
+
names are not used by derivative works. The fonts and derivatives,
|
| 25 |
+
however, cannot be released under any other type of license. The
|
| 26 |
+
requirement for fonts to remain under this license does not apply
|
| 27 |
+
to any document created using the fonts or their derivatives.
|
| 28 |
+
|
| 29 |
+
DEFINITIONS
|
| 30 |
+
"Font Software" refers to the set of files released by the Copyright
|
| 31 |
+
Holder(s) under this license and clearly marked as such. This may
|
| 32 |
+
include source files, build scripts and documentation.
|
| 33 |
+
|
| 34 |
+
"Reserved Font Name" refers to any names specified as such after the
|
| 35 |
+
copyright statement(s).
|
| 36 |
+
|
| 37 |
+
"Original Version" refers to the collection of Font Software components as
|
| 38 |
+
distributed by the Copyright Holder(s).
|
| 39 |
+
|
| 40 |
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
| 41 |
+
or substituting -- in part or in whole -- any of the components of the
|
| 42 |
+
Original Version, by changing formats or by porting the Font Software to a
|
| 43 |
+
new environment.
|
| 44 |
+
|
| 45 |
+
"Author" refers to any designer, engineer, programmer, technical
|
| 46 |
+
writer or other person who contributed to the Font Software.
|
| 47 |
+
|
| 48 |
+
PERMISSION & CONDITIONS
|
| 49 |
+
Permission is hereby granted, free of charge, to any person obtaining
|
| 50 |
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
| 51 |
+
redistribute, and sell modified and unmodified copies of the Font
|
| 52 |
+
Software, subject to the following conditions:
|
| 53 |
+
|
| 54 |
+
1) Neither the Font Software nor any of its individual components,
|
| 55 |
+
in Original or Modified Versions, may be sold by itself.
|
| 56 |
+
|
| 57 |
+
2) Original or Modified Versions of the Font Software may be bundled,
|
| 58 |
+
redistributed and/or sold with any software, provided that each copy
|
| 59 |
+
contains the above copyright notice and this license. These can be
|
| 60 |
+
included either as stand-alone text files, human-readable headers or
|
| 61 |
+
in the appropriate machine-readable metadata fields within text or
|
| 62 |
+
binary files as long as those fields can be easily viewed by the user.
|
| 63 |
+
|
| 64 |
+
3) No Modified Version of the Font Software may use the Reserved Font
|
| 65 |
+
Name(s) unless explicit written permission is granted by the corresponding
|
| 66 |
+
Copyright Holder. This restriction only applies to the primary font name as
|
| 67 |
+
presented to the users.
|
| 68 |
+
|
| 69 |
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
| 70 |
+
Software shall not be used to promote, endorse or advertise any
|
| 71 |
+
Modified Version, except to acknowledge the contribution(s) of the
|
| 72 |
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
| 73 |
+
permission.
|
| 74 |
+
|
| 75 |
+
5) The Font Software, modified or unmodified, in part or in whole,
|
| 76 |
+
must be distributed entirely under this license, and must not be
|
| 77 |
+
distributed under any other license. The requirement for fonts to
|
| 78 |
+
remain under this license does not apply to any document created
|
| 79 |
+
using the Font Software.
|
| 80 |
+
|
| 81 |
+
TERMINATION
|
| 82 |
+
This license becomes null and void if any of the above conditions are
|
| 83 |
+
not met.
|
| 84 |
+
|
| 85 |
+
DISCLAIMER
|
| 86 |
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| 87 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
| 88 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
| 89 |
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
| 90 |
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
| 91 |
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
| 92 |
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
| 93 |
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
| 94 |
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
frontend/fonts/ark-pixel-12px-proportional-ja.ttf.woff2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:523ac20546bff6438823d1c0c190ce0b286cc840789463d06b0cfa048a58d851
|
| 3 |
+
size 756500
|
frontend/fonts/ark-pixel-12px-proportional-ko.ttf.woff2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e47c469d1cad722e644e63eab1ea96499e4b1a32408790f27bd23bba6f87ec17
|
| 3 |
+
size 754640
|
frontend/fonts/ark-pixel-12px-proportional-latin.ttf.woff2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e8635d64af569b81fe314f581be23ea7ff94b530a22ae23f4fdff26a10462428
|
| 3 |
+
size 740516
|
frontend/fonts/ark-pixel-12px-proportional-zh_cn.ttf.woff2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:05a7f655ce2b88e35a2c669590e471cf55b582e3403aa58e5c76a524c53b775a
|
| 3 |
+
size 743844
|
frontend/fonts/ark-pixel-12px-proportional-zh_hk.ttf.woff2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7992fe4ab9d595aeaba6a056efd15218502fcd04cc6df225ba2722f1c5e3c5dc
|
| 3 |
+
size 752724
|
frontend/fonts/ark-pixel-12px-proportional-zh_tr.ttf.woff2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ba7cd8e2638accede72dcf4040d69e46a98dc3f551a38b05e916ae635fdecd37
|
| 3 |
+
size 760004
|
frontend/fonts/ark-pixel-12px-proportional-zh_tw.ttf.woff2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:80c591585f447a7da9a8d13220877968c3d27080108a061a9cf0ac33e2eda691
|
| 3 |
+
size 756084
|
frontend/fonts/ark-pixel-font-12px-proportional-ttf.woff2-v2025.10.20.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:08fc0408d9ac79a0f416dce254db3cea83806a9b2f823191f5d53c71daf7730f
|
| 3 |
+
size 5270031
|
frontend/guest_anim_1.webp
ADDED
|
Git LFS Details
|
frontend/guest_anim_2.webp
ADDED
|
Git LFS Details
|
frontend/guest_anim_3.webp
ADDED
|
Git LFS Details
|
frontend/guest_anim_4.webp
ADDED
|
Git LFS Details
|
frontend/guest_anim_5.webp
ADDED
|
Git LFS Details
|
frontend/guest_anim_6.webp
ADDED
|
Git LFS Details
|
frontend/guest_role_1.png
ADDED
|
Git LFS Details
|
frontend/guest_role_2.png
ADDED
|
Git LFS Details
|
frontend/guest_role_3.png
ADDED
|
Git LFS Details
|
frontend/guest_role_4.png
ADDED
|
Git LFS Details
|