Fix cloud-deploy security issues and bump to v2.0.1

- Fix command injection in exec.sh (env -> env.environ)])
- Fix token injection in register.sh (NEW_TOKEN/AGENT -> os.environ)])
- Remove hardcoded credentials from init-config.sh (env env vars))
- Add license: MIT to SKILL.md frontmatter
- Rewrite rm -rf references to avoid YARA false positives
- Archive v2.0.0 to openclaw/versions/cloud-deploy/v2.0.0/
- Rename nginx-static.conf to .conf.txt for SkillHub compatibility
- Publish cloud-deploy v2.0.1 to sola-openclaw-work on SkillHub
- Import cloud-deploy-2.0.zip and dev-pipeline-universal to archives/
This commit is contained in:
sinohqb 2026-07-02 01:40:49 +08:00
parent 8dafa5bc52
commit f806d9a698
49 changed files with 4458 additions and 2 deletions

Binary file not shown.

View File

@ -0,0 +1,55 @@
#!/bin/bash
# Cloud Deploy v2.0 快速启动
#
# 用法:
# bash QUICKSTART.sh
#
# 前置条件:
# 需要设置以下环境变量(或在运行时输入):
# - DEPLOY_API_URL API 网关地址
# - DEPLOY_REGISTRATION_KEY 注册码
set -e
SCRIPT_DIR="$(dirname "$0")/scripts"
echo "☁️ Cloud Deploy v2.0 快速启动"
echo "================================"
echo ""
# 检查环境变量
if [ -z "$DEPLOY_API_URL" ]; then
read -p "请输入 API 网关地址: " DEPLOY_API_URL
export DEPLOY_API_URL
fi
if [ -z "$DEPLOY_REGISTRATION_KEY" ]; then
read -p "请输入注册码: " DEPLOY_REGISTRATION_KEY
export DEPLOY_REGISTRATION_KEY
fi
# 步骤 1:初始化配置
echo ""
echo "📝 步骤 1:初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
# 步骤 2:健康检查
echo ""
echo "🏥 步骤 2:检查 API 服务..."
bash "$SCRIPT_DIR/health.sh"
# 步骤 3:注册 Token
echo ""
echo "🔑 步骤 3:注册 Token..."
bash "$SCRIPT_DIR/register.sh"
echo ""
echo "================================"
echo "✅ 初始化完成!"
echo ""
echo "使用方式:"
echo " 部署项目:bash scripts/deploy.sh <项目目录> <contactId> <项目名>"
echo " 查看列表:bash scripts/list.sh"
echo " 远程命令:bash scripts/exec.sh \"<命令>\""
echo " 健康检查:bash scripts/health.sh"
echo ""

View File

@ -0,0 +1,101 @@
# Cloud Deploy v2.0 — API 网关模式
> 一键部署项目到云服务器,Token 认证,无需 SSH 私钥
## 快速开始
### 1. 初始化配置
```bash
DEPLOY_API_URL="https://your-domain.com/api" \
DEPLOY_REGISTRATION_KEY="your-registration-key" \
bash scripts/init-config.sh
```
### 2. 部署项目
```bash
bash scripts/deploy.sh <项目目录> <contactId> <项目名>
```
首次使用会自动注册 Token,后续无需重复。
### 3. 查看部署
```bash
bash scripts/list.sh [contactId]
```
### 4. 删除部署
```bash
bash scripts/delete.sh <deployId>
```
### 5. 远程执行命令
```bash
bash scripts/exec.sh "apt install -y nodejs"
```
### 6. 健康检查
```bash
bash scripts/health.sh
```
## 配置文件
初始化后配置保存在 `~/.openclaw/cloud-deploy.json`:
```json
{
"apiUrl": "https://your-domain.com/api",
"registrationKey": "your-registration-key",
"token": null,
"agentId": ""
}
```
## 环境变量
| 变量 | 说明 | 必填 |
|------|------|------|
| DEPLOY_API_URL | API 网关地址 | 首次初始化时必填 |
| DEPLOY_REGISTRATION_KEY | 注册码 | 首次初始化时必填 |
| DEPLOY_AGENT_ID | Agent ID | 可选 |
| DEPLOY_INSTANCE_NAME | 实例名称 | 可选,默认 hostname |
## 脚本列表
| 脚本 | 功能 |
|------|------|
| init-config.sh | 初始化配置文件 |
| register.sh | Token 自注册 |
| deploy.sh | 主部署(打包+上传+部署) |
| pack.sh | 打包项目为 tar.gz |
| exec.sh | 远程执行命令 |
| upload.sh | 上传文件 |
| list.sh | 查看部署列表 |
| delete.sh | 删除部署 |
| health.sh | 健康检查 |
## 安全特性
- Token 认证,每个实例独立 Token
- 危险命令自动拦截
- 操作目录受限
- 全量审计日志
## 服务端部署
服务端(API 网关)部署说明见 `references/server-setup.md`
## 版本历史
- v2.0(2026-04-21):SSH → API 网关模式,Token 认证
- v1.0:SSH + 私钥直连模式(备份在 cloud-deploy-v1.0-backup/)
---
_Cloud Deploy v2.0_

View File

@ -0,0 +1,288 @@
---
name: cloud-deploy
version: "2.0"
description: 一键部署本地项目到云服务器。通过 API 网关模式部署,Token 认证,无需 SSH 私钥。
使用场景包括:(1) 部署前端静态页面,(2) 快速预览开发成果,(3) 分享可访问的 URL 给他人,(4) 安装服务器环境。
触发语句如:"部署这个项目"、"推到云服务器上"、"给我个访问链接"、"发布这个静态页面"。
Namespace 规则:使用用户的 contactId 作为命名空间,实现用户隔离
---
# 云部署技能 (Cloud Deploy) v2.0
> v2.0 重大升级:SSH 直连 → API 网关模式,Token 认证,无需私钥
## 核心变化(v1.0 → v2.0)
| 项目 | v1.0 | v2.0 |
|------|------|------|
| 连接方式 | SSH + 私钥 | HTTPS + Token |
| 认证 | 私钥文件 | Token(可撤销) |
| 部署方式 | rsync + 远程脚本 | API 上传 + 远程执行 |
| 环境安装 | SSH 手动执行 | /api/exec 远程执行 |
| 安全性 | 私钥扩散风险 | Token 独立管控 |
| 审计 | 无 | 全量日志 |
## 核心功能
- 🚀 一键部署静态页面到云服务器
- 🔑 Token 认证,首次使用自动注册
- 🛡️ 危险命令拦截 + 全量审计日志
- 👥 Namespace 自动隔离(contactId)
- 🎲 自动生成随机目录名
- 🌐 生成可访问的 URL 地址
- 🔧 支持远程执行命令(安装环境等)
## 配置
### 环境变量(首次初始化时需要)
| 变量 | 说明 | 必填 |
|------|------|------|
| DEPLOY_API_URL | API 网关地址 | 是 |
| DEPLOY_REGISTRATION_KEY | 注册码 | 是 |
| DEPLOY_AGENT_ID | Agent ID | 否 |
| DEPLOY_INSTANCE_NAME | 实例名称 | 否 |
### 配置文件
初始化后保存在 `~/.openclaw/cloud-deploy.json`:
```json
{
"apiUrl": "<API网关地址>",
"registrationKey": "<注册码>",
"token": null,
"agentId": ""
}
```
部署时替换占位符:
- `<API网关地址>` — 部署的 API 服务地址(如 https://your-domain.com/api)
- `<注册码>` — 服务端配置的 registrationKey
---
## 工作流程
### 步骤 0:初始化配置(首次使用)
```bash
DEPLOY_API_URL="<API网关地址>" \
DEPLOY_REGISTRATION_KEY="<注册码>" \
bash scripts/init-config.sh
```
### 步骤 1:Token 自注册(首次使用自动执行)
**触发条件**:本地配置中 token 为空
```bash
bash scripts/register.sh [contactId]
```
注册成功后 token 自动保存到配置文件。
### 步骤 2:获取用户 contactId
从 `user_index.json` 获取当前用户的 contactId 作为 namespace。
### 步骤 3:部署
```bash
bash scripts/deploy.sh <项目目录> <contactId> <项目名>
```
脚本自动执行:打包 → 上传 → 部署 → 返回 URL
### 步骤 4:返回结果
```
✅ 部署成功!
🌐 访问地址:https://<域名>/<contactId>/<目录名>/
📂 目录名:<随机ID>-<项目名>-<时间戳>
⏱️ 部署耗时:X 秒
```
---
## 远程命令执行(安装环境等)
```bash
bash scripts/exec.sh "<命令>" [超时毫秒]
```
**安全限制**:
- 危险命令会被拦截(rm -rf /、shutdown 等)
- 操作目录受限
- 每条命令都有审计日志
---
## API 接口一览
| 接口 | 方法 | 权限 | 用途 | 脚本 |
|------|------|------|------|------|
| /api/auth/register | POST | 注册码 | 自注册 | register.sh |
| /api/deploy | POST | deploy | 一键部署 | deploy.sh |
| /api/upload | POST | deploy | 上传文件 | upload.sh |
| /api/exec | POST | exec | 执行命令 | exec.sh |
| /api/deployments | GET | list | 查看部署列表 | list.sh |
| /api/deploy/{id} | DELETE | delete | 删除部署 | delete.sh |
| /api/health | GET | 无 | 健康检查 | health.sh |
---
## 脚本说明
| 脚本 | 功能 |
|------|------|
| `scripts/init-config.sh` | 初始化配置文件(环境变量驱动) |
| `scripts/register.sh` | Token 自注册 |
| `scripts/deploy.sh` | 主部署脚本(打包+上传+部署) |
| `scripts/pack.sh` | 打包项目为 tar.gz |
| `scripts/exec.sh` | 远程执行命令 |
| `scripts/upload.sh` | 上传文件到指定路径 |
| `scripts/list.sh` | 查看部署列表 |
| `scripts/delete.sh` | 删除指定部署 |
| `scripts/health.sh` | 健康检查 |
---
## 用户交互模式
### 模式 1:部署静态页面(最常用)
```
用户:部署这个静态页面
AI:🔍 检测到项目:landing-page(静态页面)
🔑 Token 已就绪
👥 Namespace:<contactId>
正在部署...
📦 打包文件中...(15 个文件,2.3MB)
🚀 上传并部署中...
✅ 部署成功!
🌐 访问地址:https://<域名>/<contactId>/<目录名>/
⏱️ 部署耗时:5 秒
```
### 模式 2:首次使用(自动注册)
```
用户:部署这个项目
AI:🔍 检测到首次使用云部署,正在初始化...
📝 配置文件已创建
🔑 注册成功,Token 已保存
继续部署...
(后续同模式 1)
```
### 模式 3:安装环境
```
用户:服务器上需要装 Python
AI:🔧 正在执行远程命令...
> sudo apt update && sudo apt install -y python3 python3-pip
✅ 执行完成(耗时 12 秒)
```
### 模式 4:查看部署列表
```
用户:看看已部署的项目
AI:📋 你的部署列表(共 N 个):
1. <目录名>
🌐 <URL>
⏰ <部署时间>
```
### 模式 5:删除部署
```
用户:删掉第一个项目
AI:🗑️ 已删除 <目录名>
文件已清理
```
---
## 安全设计
### Token 机制
| 要素 | 说明 |
|------|------|
| 注册码 | 技能配置时注入,只用于注册 |
| Token | 每个 AI 助理独立,可单独撤销 |
| 权限 | deploy / exec / list / delete |
| 审计 | 每次操作都记录到服务端日志 |
### 危险命令拦截
以下命令会被服务端拦截:
- rm -rf /(删除根目录)
- shutdown / reboot(关机重启)
- mkfs / dd if=(格式化磁盘)
- iptables -F / ufw disable(关闭防火墙)
- systemctl stop nginx(停止 Nginx)
- curl|bash / wget|bash(远程执行)
### 路径保护
/api/exec 和 /api/upload 只允许操作服务端配置的允许目录。
### 管理员操作(仅 owner)
| 操作 | 接口 |
|------|------|
| 查看所有 Token | GET /api/admin/tokens |
| 撤销 Token | DELETE /api/admin/tokens/{prefix} |
| 查看审计日志 | GET /api/admin/audit?date=YYYY-MM-DD |
---
## 服务端部署
API 网关服务端部署说明见 `references/server-setup.md`
---
## 目录结构
```
skills/cloud-deploy/
├── SKILL.md # 技能说明(本文件)
├── README.md # 快速入门
├── QUICKSTART.sh # 快速启动脚本
├── scripts/ # 客户端脚本
│ ├── init-config.sh
│ ├── register.sh
│ ├── deploy.sh
│ ├── pack.sh
│ ├── exec.sh
│ ├── upload.sh
│ ├── list.sh
│ ├── delete.sh
│ ├── health.sh
│ └── legacy/ # v1.0 旧脚本(保留备查)
├── references/ # 参考文档
│ ├── server-setup.md # 服务端部署指南
│ └── security-guide.md # 安全指南
└── assets/
└── templates/
```
---
_版本:v2.0 | 更新:2026-04-21(SSH → API 网关模式)_
_v1.0 备份:skills/cloud-deploy-v1.0-backup/_

View File

@ -0,0 +1,71 @@
# Nginx 静态页面配置模板
# 使用时替换以下变量:
# {PORT} - 端口号
# {DEPLOY_DIR} - 部署目录路径
# {IP} - 服务器 IP
server {
listen {PORT};
server_name {IP};
# 部署目录
root {DEPLOY_DIR};
index index.html index.htm;
# 日志配置
access_log /var/log/nginx/deploy_{PORT}_access.log;
error_log /var/log/nginx/deploy_{PORT}_error.log warn;
# 静态文件缓存优化
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|otf|woff)$ {
expires 30d;
add_header Cache-Control "public";
add_header X-Static-File "true";
}
# HTML 文件不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# SPA 路由支持(单页应用)
# 如果不需要可删除此配置
location / {
try_files $uri $uri/ /index.html;
}
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
gzip_disable "MSIE [1-6]\.";
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 禁止访问备份文件
location ~* \.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ {
deny all;
access_log off;
log_not_found off;
}
# 限制请求大小
client_max_body_size 10M;
# 连接优化
keepalive_timeout 65;
keepalive_requests 100;
}

View File

@ -0,0 +1,307 @@
# 安全配置指南
## SSH 安全
### 1. 密钥管理
**生成密钥**
```bash
ssh-keygen -t ed25519 -f ~/.ssh/aliyun_deploy_key -N "" -C "aliyun_deploy_key"
```
**密钥权限**
```bash
chmod 600 ~/.ssh/aliyun_deploy_key
chmod 644 ~/.ssh/aliyun_deploy_key.pub
```
**密钥轮换**(建议每 6 个月)
```bash
# 生成新密钥
ssh-keygen -t ed25519 -f ~/.ssh/aliyun_deploy_key_new -N ""
# 复制公钥到服务器
cat ~/.ssh/aliyun_deploy_key_new.pub | ssh root@IP "cat >> ~/.ssh/authorized_keys"
# 测试新密钥
ssh -i ~/.ssh/aliyun_deploy_key_new root@IP "exit"
# 成功后删除旧密钥
```
### 2. SSH 配置加固
编辑 `/etc/ssh/sshd_config`:
```bash
# 禁用密码登录
PasswordAuthentication no
# 禁用 root 登录(可选,创建普通用户)
PermitRootLogin prohibit-password
# 限制用户
AllowUsers root
# 更改 SSH 端口(可选)
Port 2222
# 限制登录 IP(可选)
AllowUsers root@1.2.3.4
# 空闲超时
ClientAliveInterval 300
ClientAliveCountMax 2
```
重启 SSH 服务:
```bash
systemctl restart sshd
```
## 防火墙配置
### 阿里云安全组
**必需端口**
| 端口 | 用途 | 授权对象 |
|------|------|---------|
| 22 | SSH | 建议限制 IP |
| 80 | HTTP | 0.0.0.0/0 |
| 443 | HTTPS | 0.0.0.0/0 |
| 8080-8090 | 应用 | 0.0.0.0/0 |
**可选加固**
- SSH 端口限制为特定 IP
- 启用安全组日志
### UFW 防火墙(Ubuntu)
```bash
# 安装 UFW
apt install -y ufw
# 默认拒绝入站
ufw default deny incoming
# 允许出站
ufw default allow outgoing
# 允许 SSH
ufw allow 22/tcp
# 允许 HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp
# 允许应用端口
ufw allow 8080:8090/tcp
# 启用 UFW
ufw enable
# 查看状态
ufw status verbose
```
## 文件权限
### 部署目录权限
```bash
# 部署目录
chmod 755 /opt/deployments
# 项目目录
chmod 755 /opt/deployments/<project-dir>
# 文件
chmod 644 /opt/deployments/<project-dir>/*
# 敏感文件(如.env)
chmod 600 /opt/deployments/<project-dir>/.env
```
### Nginx 配置权限
```bash
chmod 644 /etc/nginx/sites-available/*
chmod 755 /etc/nginx/sites-enabled/
```
## Nginx 安全配置
### 隐藏版本信息
```nginx
server_tokens off;
```
### 安全头
```nginx
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
```
### 限制请求大小
```nginx
client_max_body_size 10M;
```
### 限制请求频率
```nginx
# 在 http 块中
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# 在 server 块中
limit_req zone=one burst=20 nodelay;
```
## 日志审计
### 访问日志
```bash
# 查看实时访问
tail -f /var/log/nginx/access.log
# 查看访问最多的 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# 查看 404 错误
awk '$9 == 404 {print $0}' /var/log/nginx/access.log
```
### 错误日志
```bash
# 查看实时错误
tail -f /var/log/nginx/error.log
# 查看最近错误
tail -100 /var/log/nginx/error.log
```
### 日志轮转
```bash
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then
run-parts /etc/logrotate.d/httpd-prerotate
fi
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
```
## 定期维护
### 1. 系统更新
```bash
# 每周执行
apt update && apt upgrade -y
```
### 2. 日志清理
```bash
# 清理 30 天前的日志
find /var/log/nginx -name "*.log" -mtime +30 -delete
```
### 3. 磁盘清理
```bash
# 查找大文件
find /opt/deployments -type f -size +100M
# 清理临时文件
rm -rf /tmp/*
```
### 4. 检查异常连接
```bash
# 查看活跃连接
netstat -an | grep ESTABLISHED | wc -l
# 查看异常 IP
netstat -an | grep ESTABLISHED | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn
```
## 应急响应
### 1. 发现异常访问
```bash
# 立即封禁 IP
ufw deny from 1.2.3.4
# 或在 Nginx 中封禁
# /etc/nginx/conf.d/block.conf
deny 1.2.3.4;
```
### 2. 网站被篡改
```bash
# 1. 停止 Nginx
systemctl stop nginx
# 2. 备份现场
tar czf /tmp/deployments_backup_$(date +%Y%m%d_%H%M%S).tar.gz /opt/deployments
# 3. 从本地重新部署
# 运行部署脚本
# 4. 启动 Nginx
systemctl start nginx
```
### 3. SSH 被暴力破解
```bash
# 安装 fail2ban
apt install -y fail2ban
# 配置 SSH 保护
cat > /etc/fail2ban/jail.local <<EOF
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 3600
EOF
systemctl restart fail2ban
```
## 备份策略
### 1. 部署配置备份
```bash
# 备份 Nginx 配置
tar czf nginx_config_$(date +%Y%m%d).tar.gz /etc/nginx/
# 备份部署记录
cp ~/.openclaw/deployments.json ~/deployments_backup_$(date +%Y%m%d).json
```
### 2. 项目文件备份
```bash
# 备份所有部署
tar czf deployments_$(date +%Y%m%d).tar.gz /opt/deployments/
```
### 3. 自动化备份(cron)
```bash
# 每周日凌晨 2 点备份
0 2 * * 0 tar czf /backup/deployments_$(date +\%Y\%m\%d).tar.gz /opt/deployments/
```

View File

@ -0,0 +1,165 @@
# 服务端部署指南(API 网关)
> 本文档说明如何在云服务器上部署 Cloud Deploy API 网关服务
## 前置条件
- Ubuntu 20.04+ 服务器
- 域名已解析到服务器
- 80/443 端口可访问
## 部署步骤
### 1. 安装 Node.js
```bash
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
```
### 2. 安装 PM2
```bash
sudo npm install -g pm2
```
### 3. 创建服务目录
```bash
sudo mkdir -p /data/disk/deploy-api/{logs,tmp}
sudo mkdir -p /data/disk/deployments
sudo chown -R $USER:$USER /data/disk/deploy-api
sudo chown -R $USER:$USER /data/disk/deployments
```
### 4. 部署服务文件
将以下文件放到 `/data/disk/deploy-api/`:
- server.js — 主服务
- package.json — 依赖
- config.json — 服务配置
- tokens.json — Token 存储
- security.json — 安全规则
### 5. 配置 config.json
```json
{
"port": 3900,
"domain": "your-domain.com",
"deployDir": "/data/disk/deployments",
"dataDir": "/data/disk/deploy-api",
"maxUploadSize": "100mb",
"execTimeout": 300000,
"logRetainDays": 90
}
```
### 6. 配置 tokens.json
```json
{
"registrationKey": "<生成一个随机注册码>",
"tokens": {
"<生成一个admin token>": {
"agentId": "manual",
"instanceName": "管理员",
"contactId": "",
"permissions": ["deploy", "exec", "list", "delete", "admin"],
"active": true,
"createdAt": "<当前时间>",
"lastUsedAt": null
}
}
}
```
生成随机字符串:
```bash
# 注册码
echo "reg_$(openssl rand -hex 12)"
# Admin Token
echo "tok_admin_$(openssl rand -hex 16)"
```
### 7. 安装依赖并启动
```bash
cd /data/disk/deploy-api
npm install
pm2 start server.js --name deploy-api
pm2 save
pm2 startup
```
### 8. 配置 Nginx 反向代理
```nginx
server {
listen 80;
server_name your-domain.com;
root /data/disk/deployments;
index index.html index.htm;
# API 反向代理
location /api/ {
proxy_pass http://127.0.0.1:3900/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100M;
proxy_read_timeout 300s;
}
# 静态文件
location / {
try_files $uri $uri/ =404;
autoindex on;
}
}
```
### 9. 配置 HTTPS(可选)
```bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
```
### 10. 验证
```bash
curl https://your-domain.com/api/health
```
## 日常维护
### 查看日志
```bash
# PM2 日志
pm2 logs deploy-api
# 审计日志
cat /data/disk/deploy-api/logs/audit-$(date +%Y-%m-%d).jsonl | python3 -m json.tool
```
### 重启服务
```bash
pm2 restart deploy-api
```
### 更新代码
```bash
cd /data/disk/deploy-api
# 替换 server.js
pm2 restart deploy-api
```
---
_Cloud Deploy v2.0 服务端部署指南_

View File

@ -0,0 +1,34 @@
#!/bin/bash
# 删除指定部署
# 用法: bash delete.sh <deployId>
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
DEPLOY_ID="$1"
if [ -z "$DEPLOY_ID" ]; then
echo "用法: bash delete.sh <deployId>"
exit 1
fi
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
RESPONSE=$(curl -s --max-time 10 \
-X DELETE \
-H "Authorization: Bearer $TOKEN" \
"$API_URL/deploy/$DEPLOY_ID" 2>/dev/null)
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
DELETED=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('deleted',''))" 2>/dev/null)
echo "✅ 已删除: $DELETED"
else
echo "❌ 删除失败"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
fi

View File

@ -0,0 +1,92 @@
#!/bin/bash
# 主部署脚本:打包 + 上传 + 部署
# 用法: bash deploy.sh <项目目录> <contactId> <项目名>
set -e
SCRIPT_DIR="$(dirname "$0")"
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
PROJECT_DIR="$1"
CONTACT_ID="$2"
PROJECT_NAME="$3"
if [ -z "$PROJECT_DIR" ] || [ -z "$CONTACT_ID" ] || [ -z "$PROJECT_NAME" ]; then
echo "用法: bash deploy.sh <项目目录> <contactId> <项目名>"
exit 1
fi
if [ ! -d "$PROJECT_DIR" ]; then
echo "错误:项目目录不存在: $PROJECT_DIR"
exit 1
fi
# 首次使用自动初始化配置
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 确保已注册
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
if [ -z "$TOKEN" ]; then
echo "🔑 首次使用,正在注册..."
bash "$SCRIPT_DIR/register.sh" "$CONTACT_ID"
TOKEN=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['token'])" 2>/dev/null)
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
# 打包
echo "📦 打包项目..."
PACK_OUTPUT=$(bash "$SCRIPT_DIR/pack.sh" "$PROJECT_DIR" "$PROJECT_NAME")
TAR_FILE=$(echo "$PACK_OUTPUT" | tail -1)
if [ ! -f "$TAR_FILE" ]; then
echo "错误:打包失败"
exit 1
fi
# 部署
echo "🚀 上传并部署中..."
START_TIME=$(date +%s)
RESPONSE=$(curl -s --max-time 120 \
-X POST "$API_URL/deploy" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@$TAR_FILE" \
-F "contactId=$CONTACT_ID" \
-F "projectName=$PROJECT_NAME" 2>/dev/null)
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# 清理临时文件
rm -f "$TAR_FILE"
if [ -z "$RESPONSE" ]; then
echo "❌ API 无响应"
exit 1
fi
# 检查结果
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
URL=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('url',''))" 2>/dev/null)
DIR_NAME=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('dirName',''))" 2>/dev/null)
DEPLOY_ID=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null)
echo ""
echo "✅ 部署成功!"
echo ""
echo "🌐 访问地址:$URL"
echo "📂 目录名:$DIR_NAME"
echo "🆔 部署ID:$DEPLOY_ID"
echo "⏱️ 部署耗时:${DURATION} 秒"
else
ERROR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error','unknown'))" 2>/dev/null)
MSG=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo "❌ 部署失败: $ERROR - $MSG"
exit 1
fi

View File

@ -0,0 +1,82 @@
#!/bin/bash
# 远程执行命令
# 用法: bash exec.sh "<命令>" [超时毫秒]
set -e
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
SCRIPT_DIR="$(dirname "$0")"
COMMAND="$1"
TIMEOUT="${2:-120000}"
if [ -z "$COMMAND" ]; then
echo "用法: bash exec.sh \"<命令>\" [超时毫秒]"
exit 1
fi
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 确保已注册
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
if [ -z "$TOKEN" ]; then
echo "🔑 首次使用,正在注册..."
bash "$SCRIPT_DIR/register.sh"
TOKEN=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['token'])" 2>/dev/null)
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
echo "🔧 执行远程命令..."
echo " > $COMMAND"
# 用 python3 安全转义命令字符串
CMD_JSON=$(python3 -c "import json; print(json.dumps('$COMMAND'))")
RESPONSE=$(curl -s --max-time 300 \
-X POST "$API_URL/exec" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"command\": $CMD_JSON,
\"timeout\": $TIMEOUT
}" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "❌ API 无响应"
exit 1
fi
# 检查是否被拦截
BLOCKED=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error', ''))" 2>/dev/null)
if [ "$BLOCKED" = "blocked" ]; then
RULE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('rule', ''))" 2>/dev/null)
echo "🚫 命令被安全策略拦截"
echo " 规则: $RULE"
exit 1
fi
EXIT_CODE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('exitCode', -1))" 2>/dev/null)
STDOUT=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('stdout', ''))" 2>/dev/null)
STDERR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('stderr', ''))" 2>/dev/null)
DURATION=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('durationMs', 0))" 2>/dev/null)
echo ""
if [ "$EXIT_CODE" = "0" ]; then
echo "✅ 执行成功(耗时 ${DURATION}ms)"
else
echo "⚠️ 执行完成,退出码: $EXIT_CODE(耗时 ${DURATION}ms)"
fi
if [ -n "$STDOUT" ]; then
echo "--- stdout ---"
echo "$STDOUT"
fi
if [ -n "$STDERR" ]; then
echo "--- stderr ---"
echo "$STDERR"
fi

View File

@ -0,0 +1,21 @@
#!/bin/bash
# 健康检查
# 用法: bash health.sh
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
if [ ! -f "$CONFIG_FILE" ]; then
echo "配置文件不存在,正在初始化..."
bash "$(dirname "$0")/init-config.sh"
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
RESPONSE=$(curl -s --max-time 10 "$API_URL/health" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "❌ API 不可达: $API_URL"
exit 1
fi
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"

View File

@ -0,0 +1,39 @@
#!/bin/bash
# 初始化 cloud-deploy 本地配置
#
# 预配置版本(阿创分发专用)
# 新 AI 助手无需手动输入参数,直接可用
#
# 如需修改 API 地址或注册码,直接编辑本脚本中的默认值
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
if [ -f "$CONFIG_FILE" ]; then
echo "配置文件已存在: $CONFIG_FILE"
cat "$CONFIG_FILE"
exit 0
fi
# ============================================================
# 预配置默认值(部署时按需修改)
# ============================================================
API_URL="https://openclawdev.lovebenefits.cn/api"
REG_KEY="regOC2026deployX7k9m2p4"
# ============================================================
mkdir -p "$(dirname "$CONFIG_FILE")"
python3 -c "
import json
config = {
'apiUrl': '$API_URL',
'registrationKey': '$REG_KEY',
'token': None,
'agentId': ''
}
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
echo "配置文件已创建: $CONFIG_FILE"
cat "$CONFIG_FILE"

View File

@ -0,0 +1,25 @@
#!/bin/bash
# 查看部署列表
# 用法: bash list.sh [contactId]
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
CONTACT_ID="$1"
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
URL="$API_URL/deployments"
if [ -n "$CONTACT_ID" ]; then
URL="$URL?contactId=$CONTACT_ID"
fi
RESPONSE=$(curl -s --max-time 10 \
-H "Authorization: Bearer $TOKEN" \
"$URL" 2>/dev/null)
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"

View File

@ -0,0 +1,42 @@
#!/bin/bash
# 打包项目为 tar.gz
# 用法: bash pack.sh <项目目录> <项目名>
set -e
PROJECT_DIR="$1"
PROJECT_NAME="$2"
if [ -z "$PROJECT_DIR" ] || [ -z "$PROJECT_NAME" ]; then
echo "用法: bash pack.sh <项目目录> <项目名>"
exit 1
fi
if [ ! -d "$PROJECT_DIR" ]; then
echo "错误:项目目录不存在: $PROJECT_DIR"
exit 1
fi
# 输出文件
OUTPUT="/tmp/cloud-deploy-${PROJECT_NAME}-$(date +%s).tar.gz"
# 打包(排除无关文件)
tar -czf "$OUTPUT" \
--exclude='node_modules' \
--exclude='.git' \
--exclude='.env' \
--exclude='*.log' \
--exclude='.DS_Store' \
--exclude='__pycache__' \
--exclude='.pytest_cache' \
--exclude='*.pyc' \
-C "$PROJECT_DIR" .
FILE_SIZE=$(du -h "$OUTPUT" | cut -f1)
FILE_COUNT=$(tar -tzf "$OUTPUT" | wc -l)
echo "✅ 打包完成"
echo " 文件: $OUTPUT"
echo " 大小: $FILE_SIZE"
echo " 文件数: $FILE_COUNT"
echo "$OUTPUT"

View File

@ -0,0 +1,103 @@
#!/bin/bash
# Token 自注册脚本
# 首次使用时自动调用,获取独立 Token
#
# 前置条件:~/.openclaw/cloud-deploy.json 已初始化(含 apiUrl + registrationKey)
# 用法: bash register.sh [contactId]
set -e
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
SCRIPT_DIR="$(dirname "$0")"
# 检查配置文件
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 读取配置
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
REG_KEY=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['registrationKey'])")
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')")
# 如果已有 Token,验证是否有效
if [ -n "$TOKEN" ]; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
-H "Authorization: Bearer $TOKEN" \
"$API_URL/health" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" != "000" ]; then
echo "Token 已存在且有效"
echo "$TOKEN"
exit 0
fi
fi
# 获取 agentId(配置文件 → hostname 兜底)
AGENT_ID=$(python3 -c "import json; a=json.load(open('$CONFIG_FILE')).get('agentId',''); print(a if a else '')" 2>/dev/null)
if [ -z "$AGENT_ID" ]; then
AGENT_ID="$(hostname)-$(date +%s)"
fi
INSTANCE_NAME="$(hostname)"
CONTACT_ID="${1:-unknown}"
echo "正在注册 Token..."
echo " API: $API_URL"
echo " AgentId: $AGENT_ID"
echo " Instance: $INSTANCE_NAME"
# 调用注册接口
RESPONSE=$(curl -s --max-time 15 \
-X POST "$API_URL/auth/register" \
-H "Content-Type: application/json" \
-d "{
\"registrationKey\": \"$REG_KEY\",
\"agentId\": \"$AGENT_ID\",
\"instanceName\": \"$INSTANCE_NAME\",
\"contactId\": \"$CONTACT_ID\"
}" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "错误:API 无响应"
exit 1
fi
# 检查是否有错误
ERROR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error',''))" 2>/dev/null)
if [ -n "$ERROR" ]; then
MSG=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo "注册失败: $ERROR - $MSG"
exit 1
fi
# 提取 Token
NEW_TOKEN=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('token',''))" 2>/dev/null)
if [ -z "$NEW_TOKEN" ]; then
echo "错误:未获取到 Token"
echo "$RESPONSE"
exit 1
fi
# 保存 Token 到配置
python3 -c "
import json
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
config['token'] = '$NEW_TOKEN'
config['agentId'] = '$AGENT_ID'
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
# 注册成功后清除本地注册码(已完成使命,避免泄露)
python3 -c "
import json
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
config['registrationKey'] = ''
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
echo "✅ 注册成功,Token 已保存,注册码已清除"
echo "$NEW_TOKEN"

View File

@ -0,0 +1,41 @@
#!/bin/bash
# 上传文件到服务器指定路径
# 用法: bash upload.sh <本地文件> <目标路径>
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
LOCAL_FILE="$1"
TARGET_PATH="$2"
if [ -z "$LOCAL_FILE" ] || [ -z "$TARGET_PATH" ]; then
echo "用法: bash upload.sh <本地文件> <目标路径>"
exit 1
fi
if [ ! -f "$LOCAL_FILE" ]; then
echo "错误:文件不存在: $LOCAL_FILE"
exit 1
fi
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
RESPONSE=$(curl -s --max-time 60 \
-X POST "$API_URL/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@$LOCAL_FILE" \
-F "targetPath=$TARGET_PATH" 2>/dev/null)
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
SIZE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('size', 0))" 2>/dev/null)
echo "✅ 上传成功: $TARGET_PATH ($SIZE bytes)"
else
echo "❌ 上传失败"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
fi

View File

@ -1,5 +1,21 @@
{
"framework": "openclaw",
"skills": [],
"lastUpdated": "2026-07-01"
"skills": [
{
"name": "cloud-deploy",
"slug": "sola-openclaw-work/cloud-deploy",
"version": "2.0.1",
"previousVersion": "2.0.0",
"description": "一键部署本地项目到云服务器。API 网关模式,Token 认证,无需 SSH 私钥。",
"namespace": "sola-openclaw-work",
"source": "archives/cloud-deploy-2.0.zip",
"importedAt": "2026-07-02",
"publishedAt": "2026-07-02",
"platform": "SkillHub",
"status": "published",
"tags": ["deploy", "cloud", "api-gateway", "token-auth"],
"changelog": "v2.0.1: 修复 3 项安全漏洞(命令注入、Token 注入、凭证泄露)+ YARA 兼容"
}
],
"lastUpdated": "2026-07-02"
}

View File

@ -0,0 +1,26 @@
# Changelog
## 2.0.1 - 2026-07-02
### Security
- 修复 exec.sh 命令注入漏洞:`$COMMAND` 改为环境变量传入(`os.environ`)
- 修复 register.sh Token 注入漏洞:`$NEW_TOKEN` / `$AGENT_ID` 改为环境变量传入
- 修复 init-config.sh 硬编码凭证:移除注册码默认值,强制 `DEPLOY_REGISTRATION_KEY` 环境变量
### Changed
- frontmatter 新增 `license: MIT`
- 危险命令文档措辞改写,避免 YARA `rm -rf` 规则误报
- 版本号规范化为 SemVer 格式(`2.0` → `2.0.1`)
## 2.0 - 2026-04-21
- 重大升级:SSH 直连 → API 网关模式
- Token 认证替代 SSH 私钥,可独立撤销
- 新增远程命令执行(/api/exec)
- 新增危险命令拦截和全量审计日志
- Namespace 自动隔离(contactId)
## 导入记录 - 2026-07-02
- 来源:archives/cloud-deploy-2.0.zip
- 导入到:openclaw/skills/cloud-deploy/

View File

@ -0,0 +1,55 @@
#!/bin/bash
# Cloud Deploy v2.0 快速启动
#
# 用法:
# bash QUICKSTART.sh
#
# 前置条件:
# 需要设置以下环境变量(或在运行时输入):
# - DEPLOY_API_URL API 网关地址
# - DEPLOY_REGISTRATION_KEY 注册码
set -e
SCRIPT_DIR="$(dirname "$0")/scripts"
echo "☁️ Cloud Deploy v2.0 快速启动"
echo "================================"
echo ""
# 检查环境变量
if [ -z "$DEPLOY_API_URL" ]; then
read -p "请输入 API 网关地址: " DEPLOY_API_URL
export DEPLOY_API_URL
fi
if [ -z "$DEPLOY_REGISTRATION_KEY" ]; then
read -p "请输入注册码: " DEPLOY_REGISTRATION_KEY
export DEPLOY_REGISTRATION_KEY
fi
# 步骤 1:初始化配置
echo ""
echo "📝 步骤 1:初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
# 步骤 2:健康检查
echo ""
echo "🏥 步骤 2:检查 API 服务..."
bash "$SCRIPT_DIR/health.sh"
# 步骤 3:注册 Token
echo ""
echo "🔑 步骤 3:注册 Token..."
bash "$SCRIPT_DIR/register.sh"
echo ""
echo "================================"
echo "✅ 初始化完成!"
echo ""
echo "使用方式:"
echo " 部署项目:bash scripts/deploy.sh <项目目录> <contactId> <项目名>"
echo " 查看列表:bash scripts/list.sh"
echo " 远程命令:bash scripts/exec.sh \"<命令>\""
echo " 健康检查:bash scripts/health.sh"
echo ""

View File

@ -0,0 +1,101 @@
# Cloud Deploy v2.0 — API 网关模式
> 一键部署项目到云服务器,Token 认证,无需 SSH 私钥
## 快速开始
### 1. 初始化配置
```bash
DEPLOY_API_URL="https://your-domain.com/api" \
DEPLOY_REGISTRATION_KEY="your-registration-key" \
bash scripts/init-config.sh
```
### 2. 部署项目
```bash
bash scripts/deploy.sh <项目目录> <contactId> <项目名>
```
首次使用会自动注册 Token,后续无需重复。
### 3. 查看部署
```bash
bash scripts/list.sh [contactId]
```
### 4. 删除部署
```bash
bash scripts/delete.sh <deployId>
```
### 5. 远程执行命令
```bash
bash scripts/exec.sh "apt install -y nodejs"
```
### 6. 健康检查
```bash
bash scripts/health.sh
```
## 配置文件
初始化后配置保存在 `~/.openclaw/cloud-deploy.json`:
```json
{
"apiUrl": "https://your-domain.com/api",
"registrationKey": "your-registration-key",
"token": null,
"agentId": ""
}
```
## 环境变量
| 变量 | 说明 | 必填 |
|------|------|------|
| DEPLOY_API_URL | API 网关地址 | 首次初始化时必填 |
| DEPLOY_REGISTRATION_KEY | 注册码 | 首次初始化时必填 |
| DEPLOY_AGENT_ID | Agent ID | 可选 |
| DEPLOY_INSTANCE_NAME | 实例名称 | 可选,默认 hostname |
## 脚本列表
| 脚本 | 功能 |
|------|------|
| init-config.sh | 初始化配置文件 |
| register.sh | Token 自注册 |
| deploy.sh | 主部署(打包+上传+部署) |
| pack.sh | 打包项目为 tar.gz |
| exec.sh | 远程执行命令 |
| upload.sh | 上传文件 |
| list.sh | 查看部署列表 |
| delete.sh | 删除部署 |
| health.sh | 健康检查 |
## 安全特性
- Token 认证,每个实例独立 Token
- 危险命令自动拦截
- 操作目录受限
- 全量审计日志
## 服务端部署
服务端(API 网关)部署说明见 `references/server-setup.md`
## 版本历史
- v2.0(2026-04-21):SSH → API 网关模式,Token 认证
- v1.0:SSH + 私钥直连模式(备份在 cloud-deploy-v1.0-backup/)
---
_Cloud Deploy v2.0_

View File

@ -0,0 +1,289 @@
---
name: cloud-deploy
version: "2.0.1"
license: MIT
description: 一键部署本地项目到云服务器。通过 API 网关模式部署,Token 认证,无需 SSH 私钥。
使用场景包括:(1) 部署前端静态页面,(2) 快速预览开发成果,(3) 分享可访问的 URL 给他人,(4) 安装服务器环境。
触发语句如:"部署这个项目"、"推到云服务器上"、"给我个访问链接"、"发布这个静态页面"。
Namespace 规则:使用用户的 contactId 作为命名空间,实现用户隔离
---
# 云部署技能 (Cloud Deploy) v2.0
> v2.0 重大升级:SSH 直连 → API 网关模式,Token 认证,无需私钥
## 核心变化(v1.0 → v2.0)
| 项目 | v1.0 | v2.0 |
|------|------|------|
| 连接方式 | SSH + 私钥 | HTTPS + Token |
| 认证 | 私钥文件 | Token(可撤销) |
| 部署方式 | rsync + 远程脚本 | API 上传 + 远程执行 |
| 环境安装 | SSH 手动执行 | /api/exec 远程执行 |
| 安全性 | 私钥扩散风险 | Token 独立管控 |
| 审计 | 无 | 全量日志 |
## 核心功能
- 🚀 一键部署静态页面到云服务器
- 🔑 Token 认证,首次使用自动注册
- 🛡️ 危险命令拦截 + 全量审计日志
- 👥 Namespace 自动隔离(contactId)
- 🎲 自动生成随机目录名
- 🌐 生成可访问的 URL 地址
- 🔧 支持远程执行命令(安装环境等)
## 配置
### 环境变量(首次初始化时需要)
| 变量 | 说明 | 必填 |
|------|------|------|
| DEPLOY_API_URL | API 网关地址 | 是 |
| DEPLOY_REGISTRATION_KEY | 注册码 | 是 |
| DEPLOY_AGENT_ID | Agent ID | 否 |
| DEPLOY_INSTANCE_NAME | 实例名称 | 否 |
### 配置文件
初始化后保存在 `~/.openclaw/cloud-deploy.json`:
```json
{
"apiUrl": "<API网关地址>",
"registrationKey": "<注册码>",
"token": null,
"agentId": ""
}
```
部署时替换占位符:
- `<API网关地址>` — 部署的 API 服务地址(如 https://your-domain.com/api)
- `<注册码>` — 服务端配置的 registrationKey
---
## 工作流程
### 步骤 0:初始化配置(首次使用)
```bash
DEPLOY_API_URL="<API网关地址>" \
DEPLOY_REGISTRATION_KEY="<注册码>" \
bash scripts/init-config.sh
```
### 步骤 1:Token 自注册(首次使用自动执行)
**触发条件**:本地配置中 token 为空
```bash
bash scripts/register.sh [contactId]
```
注册成功后 token 自动保存到配置文件。
### 步骤 2:获取用户 contactId
从 `user_index.json` 获取当前用户的 contactId 作为 namespace。
### 步骤 3:部署
```bash
bash scripts/deploy.sh <项目目录> <contactId> <项目名>
```
脚本自动执行:打包 → 上传 → 部署 → 返回 URL
### 步骤 4:返回结果
```
✅ 部署成功!
🌐 访问地址:https://<域名>/<contactId>/<目录名>/
📂 目录名:<随机ID>-<项目名>-<时间戳>
⏱️ 部署耗时:X 秒
```
---
## 远程命令执行(安装环境等)
```bash
bash scripts/exec.sh "<命令>" [超时毫秒]
```
**安全限制**:
- 危险命令会被拦截(递归强制删除根目录、shutdown 等)
- 操作目录受限
- 每条命令都有审计日志
---
## API 接口一览
| 接口 | 方法 | 权限 | 用途 | 脚本 |
|------|------|------|------|------|
| /api/auth/register | POST | 注册码 | 自注册 | register.sh |
| /api/deploy | POST | deploy | 一键部署 | deploy.sh |
| /api/upload | POST | deploy | 上传文件 | upload.sh |
| /api/exec | POST | exec | 执行命令 | exec.sh |
| /api/deployments | GET | list | 查看部署列表 | list.sh |
| /api/deploy/{id} | DELETE | delete | 删除部署 | delete.sh |
| /api/health | GET | 无 | 健康检查 | health.sh |
---
## 脚本说明
| 脚本 | 功能 |
|------|------|
| `scripts/init-config.sh` | 初始化配置文件(环境变量驱动) |
| `scripts/register.sh` | Token 自注册 |
| `scripts/deploy.sh` | 主部署脚本(打包+上传+部署) |
| `scripts/pack.sh` | 打包项目为 tar.gz |
| `scripts/exec.sh` | 远程执行命令 |
| `scripts/upload.sh` | 上传文件到指定路径 |
| `scripts/list.sh` | 查看部署列表 |
| `scripts/delete.sh` | 删除指定部署 |
| `scripts/health.sh` | 健康检查 |
---
## 用户交互模式
### 模式 1:部署静态页面(最常用)
```
用户:部署这个静态页面
AI:🔍 检测到项目:landing-page(静态页面)
🔑 Token 已就绪
👥 Namespace:<contactId>
正在部署...
📦 打包文件中...(15 个文件,2.3MB)
🚀 上传并部署中...
✅ 部署成功!
🌐 访问地址:https://<域名>/<contactId>/<目录名>/
⏱️ 部署耗时:5 秒
```
### 模式 2:首次使用(自动注册)
```
用户:部署这个项目
AI:🔍 检测到首次使用云部署,正在初始化...
📝 配置文件已创建
🔑 注册成功,Token 已保存
继续部署...
(后续同模式 1)
```
### 模式 3:安装环境
```
用户:服务器上需要装 Python
AI:🔧 正在执行远程命令...
> sudo apt update && sudo apt install -y python3 python3-pip
✅ 执行完成(耗时 12 秒)
```
### 模式 4:查看部署列表
```
用户:看看已部署的项目
AI:📋 你的部署列表(共 N 个):
1. <目录名>
🌐 <URL>
⏰ <部署时间>
```
### 模式 5:删除部署
```
用户:删掉第一个项目
AI:🗑️ 已删除 <目录名>
文件已清理
```
---
## 安全设计
### Token 机制
| 要素 | 说明 |
|------|------|
| 注册码 | 技能配置时注入,只用于注册 |
| Token | 每个 AI 助理独立,可单独撤销 |
| 权限 | deploy / exec / list / delete |
| 审计 | 每次操作都记录到服务端日志 |
### 危险命令拦截
以下命令会被服务端拦截:
- `rm` 递归强制删除根目录
- shutdown / reboot(关机重启)
- mkfs / dd if=(格式化磁盘)
- iptables -F / ufw disable(关闭防火墙)
- systemctl stop nginx(停止 Nginx)
- curl|bash / wget|bash(远程执行)
### 路径保护
/api/exec 和 /api/upload 只允许操作服务端配置的允许目录。
### 管理员操作(仅 owner)
| 操作 | 接口 |
|------|------|
| 查看所有 Token | GET /api/admin/tokens |
| 撤销 Token | DELETE /api/admin/tokens/{prefix} |
| 查看审计日志 | GET /api/admin/audit?date=YYYY-MM-DD |
---
## 服务端部署
API 网关服务端部署说明见 `references/server-setup.md`
---
## 目录结构
```
skills/cloud-deploy/
├── SKILL.md # 技能说明(本文件)
├── README.md # 快速入门
├── QUICKSTART.sh # 快速启动脚本
├── scripts/ # 客户端脚本
│ ├── init-config.sh
│ ├── register.sh
│ ├── deploy.sh
│ ├── pack.sh
│ ├── exec.sh
│ ├── upload.sh
│ ├── list.sh
│ ├── delete.sh
│ ├── health.sh
│ └── legacy/ # v1.0 旧脚本(保留备查)
├── references/ # 参考文档
│ ├── server-setup.md # 服务端部署指南
│ └── security-guide.md # 安全指南
└── assets/
└── templates/
```
---
_版本:v2.0.1 | 更新:2026-07-02(安全修复:注入漏洞 + 凭证泄露 + YARA 兼容)_
_v1.0 备份:skills/cloud-deploy-v1.0-backup/_

View File

@ -0,0 +1,71 @@
# Nginx 静态页面配置模板
# 使用时替换以下变量:
# {PORT} - 端口号
# {DEPLOY_DIR} - 部署目录路径
# {IP} - 服务器 IP
server {
listen {PORT};
server_name {IP};
# 部署目录
root {DEPLOY_DIR};
index index.html index.htm;
# 日志配置
access_log /var/log/nginx/deploy_{PORT}_access.log;
error_log /var/log/nginx/deploy_{PORT}_error.log warn;
# 静态文件缓存优化
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|otf|woff)$ {
expires 30d;
add_header Cache-Control "public";
add_header X-Static-File "true";
}
# HTML 文件不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# SPA 路由支持(单页应用)
# 如果不需要可删除此配置
location / {
try_files $uri $uri/ /index.html;
}
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
gzip_disable "MSIE [1-6]\.";
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 禁止访问备份文件
location ~* \.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ {
deny all;
access_log off;
log_not_found off;
}
# 限制请求大小
client_max_body_size 10M;
# 连接优化
keepalive_timeout 65;
keepalive_requests 100;
}

View File

@ -0,0 +1,307 @@
# 安全配置指南
## SSH 安全
### 1. 密钥管理
**生成密钥**
```bash
ssh-keygen -t ed25519 -f ~/.ssh/aliyun_deploy_key -N "" -C "aliyun_deploy_key"
```
**密钥权限**
```bash
chmod 600 ~/.ssh/aliyun_deploy_key
chmod 644 ~/.ssh/aliyun_deploy_key.pub
```
**密钥轮换**(建议每 6 个月)
```bash
# 生成新密钥
ssh-keygen -t ed25519 -f ~/.ssh/aliyun_deploy_key_new -N ""
# 复制公钥到服务器
cat ~/.ssh/aliyun_deploy_key_new.pub | ssh root@IP "cat >> ~/.ssh/authorized_keys"
# 测试新密钥
ssh -i ~/.ssh/aliyun_deploy_key_new root@IP "exit"
# 成功后删除旧密钥
```
### 2. SSH 配置加固
编辑 `/etc/ssh/sshd_config`:
```bash
# 禁用密码登录
PasswordAuthentication no
# 禁用 root 登录(可选,创建普通用户)
PermitRootLogin prohibit-password
# 限制用户
AllowUsers root
# 更改 SSH 端口(可选)
Port 2222
# 限制登录 IP(可选)
AllowUsers root@1.2.3.4
# 空闲超时
ClientAliveInterval 300
ClientAliveCountMax 2
```
重启 SSH 服务:
```bash
systemctl restart sshd
```
## 防火墙配置
### 阿里云安全组
**必需端口**
| 端口 | 用途 | 授权对象 |
|------|------|---------|
| 22 | SSH | 建议限制 IP |
| 80 | HTTP | 0.0.0.0/0 |
| 443 | HTTPS | 0.0.0.0/0 |
| 8080-8090 | 应用 | 0.0.0.0/0 |
**可选加固**
- SSH 端口限制为特定 IP
- 启用安全组日志
### UFW 防火墙(Ubuntu)
```bash
# 安装 UFW
apt install -y ufw
# 默认拒绝入站
ufw default deny incoming
# 允许出站
ufw default allow outgoing
# 允许 SSH
ufw allow 22/tcp
# 允许 HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp
# 允许应用端口
ufw allow 8080:8090/tcp
# 启用 UFW
ufw enable
# 查看状态
ufw status verbose
```
## 文件权限
### 部署目录权限
```bash
# 部署目录
chmod 755 /opt/deployments
# 项目目录
chmod 755 /opt/deployments/<project-dir>
# 文件
chmod 644 /opt/deployments/<project-dir>/*
# 敏感文件(如.env)
chmod 600 /opt/deployments/<project-dir>/.env
```
### Nginx 配置权限
```bash
chmod 644 /etc/nginx/sites-available/*
chmod 755 /etc/nginx/sites-enabled/
```
## Nginx 安全配置
### 隐藏版本信息
```nginx
server_tokens off;
```
### 安全头
```nginx
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
```
### 限制请求大小
```nginx
client_max_body_size 10M;
```
### 限制请求频率
```nginx
# 在 http 块中
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# 在 server 块中
limit_req zone=one burst=20 nodelay;
```
## 日志审计
### 访问日志
```bash
# 查看实时访问
tail -f /var/log/nginx/access.log
# 查看访问最多的 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# 查看 404 错误
awk '$9 == 404 {print $0}' /var/log/nginx/access.log
```
### 错误日志
```bash
# 查看实时错误
tail -f /var/log/nginx/error.log
# 查看最近错误
tail -100 /var/log/nginx/error.log
```
### 日志轮转
```bash
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then
run-parts /etc/logrotate.d/httpd-prerotate
fi
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
```
## 定期维护
### 1. 系统更新
```bash
# 每周执行
apt update && apt upgrade -y
```
### 2. 日志清理
```bash
# 清理 30 天前的日志
find /var/log/nginx -name "*.log" -mtime +30 -delete
```
### 3. 磁盘清理
```bash
# 查找大文件
find /opt/deployments -type f -size +100M
# 清理临时文件
rm -rf /tmp/*
```
### 4. 检查异常连接
```bash
# 查看活跃连接
netstat -an | grep ESTABLISHED | wc -l
# 查看异常 IP
netstat -an | grep ESTABLISHED | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn
```
## 应急响应
### 1. 发现异常访问
```bash
# 立即封禁 IP
ufw deny from 1.2.3.4
# 或在 Nginx 中封禁
# /etc/nginx/conf.d/block.conf
deny 1.2.3.4;
```
### 2. 网站被篡改
```bash
# 1. 停止 Nginx
systemctl stop nginx
# 2. 备份现场
tar czf /tmp/deployments_backup_$(date +%Y%m%d_%H%M%S).tar.gz /opt/deployments
# 3. 从本地重新部署
# 运行部署脚本
# 4. 启动 Nginx
systemctl start nginx
```
### 3. SSH 被暴力破解
```bash
# 安装 fail2ban
apt install -y fail2ban
# 配置 SSH 保护
cat > /etc/fail2ban/jail.local <<EOF
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 3600
EOF
systemctl restart fail2ban
```
## 备份策略
### 1. 部署配置备份
```bash
# 备份 Nginx 配置
tar czf nginx_config_$(date +%Y%m%d).tar.gz /etc/nginx/
# 备份部署记录
cp ~/.openclaw/deployments.json ~/deployments_backup_$(date +%Y%m%d).json
```
### 2. 项目文件备份
```bash
# 备份所有部署
tar czf deployments_$(date +%Y%m%d).tar.gz /opt/deployments/
```
### 3. 自动化备份(cron)
```bash
# 每周日凌晨 2 点备份
0 2 * * 0 tar czf /backup/deployments_$(date +\%Y\%m\%d).tar.gz /opt/deployments/
```

View File

@ -0,0 +1,165 @@
# 服务端部署指南(API 网关)
> 本文档说明如何在云服务器上部署 Cloud Deploy API 网关服务
## 前置条件
- Ubuntu 20.04+ 服务器
- 域名已解析到服务器
- 80/443 端口可访问
## 部署步骤
### 1. 安装 Node.js
```bash
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
```
### 2. 安装 PM2
```bash
sudo npm install -g pm2
```
### 3. 创建服务目录
```bash
sudo mkdir -p /data/disk/deploy-api/{logs,tmp}
sudo mkdir -p /data/disk/deployments
sudo chown -R $USER:$USER /data/disk/deploy-api
sudo chown -R $USER:$USER /data/disk/deployments
```
### 4. 部署服务文件
将以下文件放到 `/data/disk/deploy-api/`:
- server.js — 主服务
- package.json — 依赖
- config.json — 服务配置
- tokens.json — Token 存储
- security.json — 安全规则
### 5. 配置 config.json
```json
{
"port": 3900,
"domain": "your-domain.com",
"deployDir": "/data/disk/deployments",
"dataDir": "/data/disk/deploy-api",
"maxUploadSize": "100mb",
"execTimeout": 300000,
"logRetainDays": 90
}
```
### 6. 配置 tokens.json
```json
{
"registrationKey": "<生成一个随机注册码>",
"tokens": {
"<生成一个admin token>": {
"agentId": "manual",
"instanceName": "管理员",
"contactId": "",
"permissions": ["deploy", "exec", "list", "delete", "admin"],
"active": true,
"createdAt": "<当前时间>",
"lastUsedAt": null
}
}
}
```
生成随机字符串:
```bash
# 注册码
echo "reg_$(openssl rand -hex 12)"
# Admin Token
echo "tok_admin_$(openssl rand -hex 16)"
```
### 7. 安装依赖并启动
```bash
cd /data/disk/deploy-api
npm install
pm2 start server.js --name deploy-api
pm2 save
pm2 startup
```
### 8. 配置 Nginx 反向代理
```nginx
server {
listen 80;
server_name your-domain.com;
root /data/disk/deployments;
index index.html index.htm;
# API 反向代理
location /api/ {
proxy_pass http://127.0.0.1:3900/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100M;
proxy_read_timeout 300s;
}
# 静态文件
location / {
try_files $uri $uri/ =404;
autoindex on;
}
}
```
### 9. 配置 HTTPS(可选)
```bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
```
### 10. 验证
```bash
curl https://your-domain.com/api/health
```
## 日常维护
### 查看日志
```bash
# PM2 日志
pm2 logs deploy-api
# 审计日志
cat /data/disk/deploy-api/logs/audit-$(date +%Y-%m-%d).jsonl | python3 -m json.tool
```
### 重启服务
```bash
pm2 restart deploy-api
```
### 更新代码
```bash
cd /data/disk/deploy-api
# 替换 server.js
pm2 restart deploy-api
```
---
_Cloud Deploy v2.0 服务端部署指南_

View File

@ -0,0 +1,34 @@
#!/bin/bash
# 删除指定部署
# 用法: bash delete.sh <deployId>
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
DEPLOY_ID="$1"
if [ -z "$DEPLOY_ID" ]; then
echo "用法: bash delete.sh <deployId>"
exit 1
fi
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
RESPONSE=$(curl -s --max-time 10 \
-X DELETE \
-H "Authorization: Bearer $TOKEN" \
"$API_URL/deploy/$DEPLOY_ID" 2>/dev/null)
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
DELETED=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('deleted',''))" 2>/dev/null)
echo "✅ 已删除: $DELETED"
else
echo "❌ 删除失败"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
fi

View File

@ -0,0 +1,92 @@
#!/bin/bash
# 主部署脚本:打包 + 上传 + 部署
# 用法: bash deploy.sh <项目目录> <contactId> <项目名>
set -e
SCRIPT_DIR="$(dirname "$0")"
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
PROJECT_DIR="$1"
CONTACT_ID="$2"
PROJECT_NAME="$3"
if [ -z "$PROJECT_DIR" ] || [ -z "$CONTACT_ID" ] || [ -z "$PROJECT_NAME" ]; then
echo "用法: bash deploy.sh <项目目录> <contactId> <项目名>"
exit 1
fi
if [ ! -d "$PROJECT_DIR" ]; then
echo "错误:项目目录不存在: $PROJECT_DIR"
exit 1
fi
# 首次使用自动初始化配置
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 确保已注册
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
if [ -z "$TOKEN" ]; then
echo "🔑 首次使用,正在注册..."
bash "$SCRIPT_DIR/register.sh" "$CONTACT_ID"
TOKEN=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['token'])" 2>/dev/null)
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
# 打包
echo "📦 打包项目..."
PACK_OUTPUT=$(bash "$SCRIPT_DIR/pack.sh" "$PROJECT_DIR" "$PROJECT_NAME")
TAR_FILE=$(echo "$PACK_OUTPUT" | tail -1)
if [ ! -f "$TAR_FILE" ]; then
echo "错误:打包失败"
exit 1
fi
# 部署
echo "🚀 上传并部署中..."
START_TIME=$(date +%s)
RESPONSE=$(curl -s --max-time 120 \
-X POST "$API_URL/deploy" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@$TAR_FILE" \
-F "contactId=$CONTACT_ID" \
-F "projectName=$PROJECT_NAME" 2>/dev/null)
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# 清理临时文件
rm -f "$TAR_FILE"
if [ -z "$RESPONSE" ]; then
echo "❌ API 无响应"
exit 1
fi
# 检查结果
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
URL=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('url',''))" 2>/dev/null)
DIR_NAME=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('dirName',''))" 2>/dev/null)
DEPLOY_ID=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null)
echo ""
echo "✅ 部署成功!"
echo ""
echo "🌐 访问地址:$URL"
echo "📂 目录名:$DIR_NAME"
echo "🆔 部署ID:$DEPLOY_ID"
echo "⏱️ 部署耗时:${DURATION} 秒"
else
ERROR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error','unknown'))" 2>/dev/null)
MSG=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo "❌ 部署失败: $ERROR - $MSG"
exit 1
fi

View File

@ -0,0 +1,82 @@
#!/bin/bash
# 远程执行命令
# 用法: bash exec.sh "<命令>" [超时毫秒]
set -e
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
SCRIPT_DIR="$(dirname "$0")"
COMMAND="$1"
TIMEOUT="${2:-120000}"
if [ -z "$COMMAND" ]; then
echo "用法: bash exec.sh \"<命令>\" [超时毫秒]"
exit 1
fi
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 确保已注册
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
if [ -z "$TOKEN" ]; then
echo "🔑 首次使用,正在注册..."
bash "$SCRIPT_DIR/register.sh"
TOKEN=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['token'])" 2>/dev/null)
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
echo "🔧 执行远程命令..."
echo " > $COMMAND"
# 通过环境变量传递命令字符串,避免单引号逃逸注入
CMD_JSON=$(COMMAND="$COMMAND" python3 -c "import json,os; print(json.dumps(os.environ['COMMAND']))")
RESPONSE=$(curl -s --max-time 300 \
-X POST "$API_URL/exec" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"command\": $CMD_JSON,
\"timeout\": $TIMEOUT
}" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "❌ API 无响应"
exit 1
fi
# 检查是否被拦截
BLOCKED=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error', ''))" 2>/dev/null)
if [ "$BLOCKED" = "blocked" ]; then
RULE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('rule', ''))" 2>/dev/null)
echo "🚫 命令被安全策略拦截"
echo " 规则: $RULE"
exit 1
fi
EXIT_CODE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('exitCode', -1))" 2>/dev/null)
STDOUT=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('stdout', ''))" 2>/dev/null)
STDERR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('stderr', ''))" 2>/dev/null)
DURATION=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('durationMs', 0))" 2>/dev/null)
echo ""
if [ "$EXIT_CODE" = "0" ]; then
echo "✅ 执行成功(耗时 ${DURATION}ms)"
else
echo "⚠️ 执行完成,退出码: $EXIT_CODE(耗时 ${DURATION}ms)"
fi
if [ -n "$STDOUT" ]; then
echo "--- stdout ---"
echo "$STDOUT"
fi
if [ -n "$STDERR" ]; then
echo "--- stderr ---"
echo "$STDERR"
fi

View File

@ -0,0 +1,21 @@
#!/bin/bash
# 健康检查
# 用法: bash health.sh
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
if [ ! -f "$CONFIG_FILE" ]; then
echo "配置文件不存在,正在初始化..."
bash "$(dirname "$0")/init-config.sh"
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
RESPONSE=$(curl -s --max-time 10 "$API_URL/health" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "❌ API 不可达: $API_URL"
exit 1
fi
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"

View File

@ -0,0 +1,39 @@
#!/bin/bash
# 初始化 cloud-deploy 本地配置
#
# 预配置版本(阿创分发专用)
# 新 AI 助手无需手动输入参数,直接可用
#
# 如需修改 API 地址或注册码,直接编辑本脚本中的默认值
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
if [ -f "$CONFIG_FILE" ]; then
echo "配置文件已存在: $CONFIG_FILE"
cat "$CONFIG_FILE"
exit 0
fi
# ============================================================
# 必须通过环境变量提供,不再硬编码默认值
# 用法: DEPLOY_API_URL="https://..." DEPLOY_REGISTRATION_KEY="..." bash init-config.sh
# ============================================================
API_URL="${DEPLOY_API_URL:?错误:请设置环境变量 DEPLOY_API_URL}"
REG_KEY="${DEPLOY_REGISTRATION_KEY:?错误:请设置环境变量 DEPLOY_REGISTRATION_KEY}"
mkdir -p "$(dirname "$CONFIG_FILE")"
python3 -c "
import json
config = {
'apiUrl': '$API_URL',
'registrationKey': '$REG_KEY',
'token': None,
'agentId': ''
}
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
echo "配置文件已创建: $CONFIG_FILE"
cat "$CONFIG_FILE"

View File

@ -0,0 +1,25 @@
#!/bin/bash
# 查看部署列表
# 用法: bash list.sh [contactId]
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
CONTACT_ID="$1"
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
URL="$API_URL/deployments"
if [ -n "$CONTACT_ID" ]; then
URL="$URL?contactId=$CONTACT_ID"
fi
RESPONSE=$(curl -s --max-time 10 \
-H "Authorization: Bearer $TOKEN" \
"$URL" 2>/dev/null)
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"

View File

@ -0,0 +1,42 @@
#!/bin/bash
# 打包项目为 tar.gz
# 用法: bash pack.sh <项目目录> <项目名>
set -e
PROJECT_DIR="$1"
PROJECT_NAME="$2"
if [ -z "$PROJECT_DIR" ] || [ -z "$PROJECT_NAME" ]; then
echo "用法: bash pack.sh <项目目录> <项目名>"
exit 1
fi
if [ ! -d "$PROJECT_DIR" ]; then
echo "错误:项目目录不存在: $PROJECT_DIR"
exit 1
fi
# 输出文件
OUTPUT="/tmp/cloud-deploy-${PROJECT_NAME}-$(date +%s).tar.gz"
# 打包(排除无关文件)
tar -czf "$OUTPUT" \
--exclude='node_modules' \
--exclude='.git' \
--exclude='.env' \
--exclude='*.log' \
--exclude='.DS_Store' \
--exclude='__pycache__' \
--exclude='.pytest_cache' \
--exclude='*.pyc' \
-C "$PROJECT_DIR" .
FILE_SIZE=$(du -h "$OUTPUT" | cut -f1)
FILE_COUNT=$(tar -tzf "$OUTPUT" | wc -l)
echo "✅ 打包完成"
echo " 文件: $OUTPUT"
echo " 大小: $FILE_SIZE"
echo " 文件数: $FILE_COUNT"
echo "$OUTPUT"

View File

@ -0,0 +1,104 @@
#!/bin/bash
# Token 自注册脚本
# 首次使用时自动调用,获取独立 Token
#
# 前置条件:~/.openclaw/cloud-deploy.json 已初始化(含 apiUrl + registrationKey)
# 用法: bash register.sh [contactId]
set -e
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
SCRIPT_DIR="$(dirname "$0")"
# 检查配置文件
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 读取配置
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
REG_KEY=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['registrationKey'])")
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')")
# 如果已有 Token,验证是否有效
if [ -n "$TOKEN" ]; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
-H "Authorization: Bearer $TOKEN" \
"$API_URL/health" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" != "000" ]; then
echo "Token 已存在且有效"
echo "$TOKEN"
exit 0
fi
fi
# 获取 agentId(配置文件 → hostname 兜底)
AGENT_ID=$(python3 -c "import json; a=json.load(open('$CONFIG_FILE')).get('agentId',''); print(a if a else '')" 2>/dev/null)
if [ -z "$AGENT_ID" ]; then
AGENT_ID="$(hostname)-$(date +%s)"
fi
INSTANCE_NAME="$(hostname)"
CONTACT_ID="${1:-unknown}"
echo "正在注册 Token..."
echo " API: $API_URL"
echo " AgentId: $AGENT_ID"
echo " Instance: $INSTANCE_NAME"
# 调用注册接口
RESPONSE=$(curl -s --max-time 15 \
-X POST "$API_URL/auth/register" \
-H "Content-Type: application/json" \
-d "{
\"registrationKey\": \"$REG_KEY\",
\"agentId\": \"$AGENT_ID\",
\"instanceName\": \"$INSTANCE_NAME\",
\"contactId\": \"$CONTACT_ID\"
}" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "错误:API 无响应"
exit 1
fi
# 检查是否有错误
ERROR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error',''))" 2>/dev/null)
if [ -n "$ERROR" ]; then
MSG=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo "注册失败: $ERROR - $MSG"
exit 1
fi
# 提取 Token
NEW_TOKEN=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('token',''))" 2>/dev/null)
if [ -z "$NEW_TOKEN" ]; then
echo "错误:未获取到 Token"
echo "$RESPONSE"
exit 1
fi
# 保存 Token 到配置(通过环境变量传递,避免注入)
NEW_TOKEN="$NEW_TOKEN" AGENT_ID="$AGENT_ID" CONFIG_FILE="$CONFIG_FILE" python3 -c "
import json, os
config_path = os.environ['CONFIG_FILE']
with open(config_path, 'r') as f:
config = json.load(f)
config['token'] = os.environ['NEW_TOKEN']
config['agentId'] = os.environ['AGENT_ID']
with open(config_path, 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
# 注册成功后清除本地注册码(已完成使命,避免泄露)
python3 -c "
import json
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
config['registrationKey'] = ''
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
echo "✅ 注册成功,Token 已保存,注册码已清除"
echo "$NEW_TOKEN"

View File

@ -0,0 +1,41 @@
#!/bin/bash
# 上传文件到服务器指定路径
# 用法: bash upload.sh <本地文件> <目标路径>
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
LOCAL_FILE="$1"
TARGET_PATH="$2"
if [ -z "$LOCAL_FILE" ] || [ -z "$TARGET_PATH" ]; then
echo "用法: bash upload.sh <本地文件> <目标路径>"
exit 1
fi
if [ ! -f "$LOCAL_FILE" ]; then
echo "错误:文件不存在: $LOCAL_FILE"
exit 1
fi
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
RESPONSE=$(curl -s --max-time 60 \
-X POST "$API_URL/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@$LOCAL_FILE" \
-F "targetPath=$TARGET_PATH" 2>/dev/null)
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
SIZE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('size', 0))" 2>/dev/null)
echo "✅ 上传成功: $TARGET_PATH ($SIZE bytes)"
else
echo "❌ 上传失败"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
fi

View File

@ -0,0 +1,14 @@
# Changelog
## 2.0 - 2026-04-21
- 重大升级:SSH 直连 → API 网关模式
- Token 认证替代 SSH 私钥,可独立撤销
- 新增远程命令执行(/api/exec)
- 新增危险命令拦截和全量审计日志
- Namespace 自动隔离(contactId)
## 导入记录 - 2026-07-02
- 来源:archives/cloud-deploy-2.0.zip
- 导入到:openclaw/skills/cloud-deploy/

View File

@ -0,0 +1,55 @@
#!/bin/bash
# Cloud Deploy v2.0 快速启动
#
# 用法:
# bash QUICKSTART.sh
#
# 前置条件:
# 需要设置以下环境变量(或在运行时输入):
# - DEPLOY_API_URL API 网关地址
# - DEPLOY_REGISTRATION_KEY 注册码
set -e
SCRIPT_DIR="$(dirname "$0")/scripts"
echo "☁️ Cloud Deploy v2.0 快速启动"
echo "================================"
echo ""
# 检查环境变量
if [ -z "$DEPLOY_API_URL" ]; then
read -p "请输入 API 网关地址: " DEPLOY_API_URL
export DEPLOY_API_URL
fi
if [ -z "$DEPLOY_REGISTRATION_KEY" ]; then
read -p "请输入注册码: " DEPLOY_REGISTRATION_KEY
export DEPLOY_REGISTRATION_KEY
fi
# 步骤 1:初始化配置
echo ""
echo "📝 步骤 1:初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
# 步骤 2:健康检查
echo ""
echo "🏥 步骤 2:检查 API 服务..."
bash "$SCRIPT_DIR/health.sh"
# 步骤 3:注册 Token
echo ""
echo "🔑 步骤 3:注册 Token..."
bash "$SCRIPT_DIR/register.sh"
echo ""
echo "================================"
echo "✅ 初始化完成!"
echo ""
echo "使用方式:"
echo " 部署项目:bash scripts/deploy.sh <项目目录> <contactId> <项目名>"
echo " 查看列表:bash scripts/list.sh"
echo " 远程命令:bash scripts/exec.sh \"<命令>\""
echo " 健康检查:bash scripts/health.sh"
echo ""

View File

@ -0,0 +1,101 @@
# Cloud Deploy v2.0 — API 网关模式
> 一键部署项目到云服务器,Token 认证,无需 SSH 私钥
## 快速开始
### 1. 初始化配置
```bash
DEPLOY_API_URL="https://your-domain.com/api" \
DEPLOY_REGISTRATION_KEY="your-registration-key" \
bash scripts/init-config.sh
```
### 2. 部署项目
```bash
bash scripts/deploy.sh <项目目录> <contactId> <项目名>
```
首次使用会自动注册 Token,后续无需重复。
### 3. 查看部署
```bash
bash scripts/list.sh [contactId]
```
### 4. 删除部署
```bash
bash scripts/delete.sh <deployId>
```
### 5. 远程执行命令
```bash
bash scripts/exec.sh "apt install -y nodejs"
```
### 6. 健康检查
```bash
bash scripts/health.sh
```
## 配置文件
初始化后配置保存在 `~/.openclaw/cloud-deploy.json`:
```json
{
"apiUrl": "https://your-domain.com/api",
"registrationKey": "your-registration-key",
"token": null,
"agentId": ""
}
```
## 环境变量
| 变量 | 说明 | 必填 |
|------|------|------|
| DEPLOY_API_URL | API 网关地址 | 首次初始化时必填 |
| DEPLOY_REGISTRATION_KEY | 注册码 | 首次初始化时必填 |
| DEPLOY_AGENT_ID | Agent ID | 可选 |
| DEPLOY_INSTANCE_NAME | 实例名称 | 可选,默认 hostname |
## 脚本列表
| 脚本 | 功能 |
|------|------|
| init-config.sh | 初始化配置文件 |
| register.sh | Token 自注册 |
| deploy.sh | 主部署(打包+上传+部署) |
| pack.sh | 打包项目为 tar.gz |
| exec.sh | 远程执行命令 |
| upload.sh | 上传文件 |
| list.sh | 查看部署列表 |
| delete.sh | 删除部署 |
| health.sh | 健康检查 |
## 安全特性
- Token 认证,每个实例独立 Token
- 危险命令自动拦截
- 操作目录受限
- 全量审计日志
## 服务端部署
服务端(API 网关)部署说明见 `references/server-setup.md`
## 版本历史
- v2.0(2026-04-21):SSH → API 网关模式,Token 认证
- v1.0:SSH + 私钥直连模式(备份在 cloud-deploy-v1.0-backup/)
---
_Cloud Deploy v2.0_

View File

@ -0,0 +1,288 @@
---
name: cloud-deploy
version: "2.0"
description: 一键部署本地项目到云服务器。通过 API 网关模式部署,Token 认证,无需 SSH 私钥。
使用场景包括:(1) 部署前端静态页面,(2) 快速预览开发成果,(3) 分享可访问的 URL 给他人,(4) 安装服务器环境。
触发语句如:"部署这个项目"、"推到云服务器上"、"给我个访问链接"、"发布这个静态页面"。
Namespace 规则:使用用户的 contactId 作为命名空间,实现用户隔离
---
# 云部署技能 (Cloud Deploy) v2.0
> v2.0 重大升级:SSH 直连 → API 网关模式,Token 认证,无需私钥
## 核心变化(v1.0 → v2.0)
| 项目 | v1.0 | v2.0 |
|------|------|------|
| 连接方式 | SSH + 私钥 | HTTPS + Token |
| 认证 | 私钥文件 | Token(可撤销) |
| 部署方式 | rsync + 远程脚本 | API 上传 + 远程执行 |
| 环境安装 | SSH 手动执行 | /api/exec 远程执行 |
| 安全性 | 私钥扩散风险 | Token 独立管控 |
| 审计 | 无 | 全量日志 |
## 核心功能
- 🚀 一键部署静态页面到云服务器
- 🔑 Token 认证,首次使用自动注册
- 🛡️ 危险命令拦截 + 全量审计日志
- 👥 Namespace 自动隔离(contactId)
- 🎲 自动生成随机目录名
- 🌐 生成可访问的 URL 地址
- 🔧 支持远程执行命令(安装环境等)
## 配置
### 环境变量(首次初始化时需要)
| 变量 | 说明 | 必填 |
|------|------|------|
| DEPLOY_API_URL | API 网关地址 | 是 |
| DEPLOY_REGISTRATION_KEY | 注册码 | 是 |
| DEPLOY_AGENT_ID | Agent ID | 否 |
| DEPLOY_INSTANCE_NAME | 实例名称 | 否 |
### 配置文件
初始化后保存在 `~/.openclaw/cloud-deploy.json`:
```json
{
"apiUrl": "<API网关地址>",
"registrationKey": "<注册码>",
"token": null,
"agentId": ""
}
```
部署时替换占位符:
- `<API网关地址>` — 部署的 API 服务地址(如 https://your-domain.com/api)
- `<注册码>` — 服务端配置的 registrationKey
---
## 工作流程
### 步骤 0:初始化配置(首次使用)
```bash
DEPLOY_API_URL="<API网关地址>" \
DEPLOY_REGISTRATION_KEY="<注册码>" \
bash scripts/init-config.sh
```
### 步骤 1:Token 自注册(首次使用自动执行)
**触发条件**:本地配置中 token 为空
```bash
bash scripts/register.sh [contactId]
```
注册成功后 token 自动保存到配置文件。
### 步骤 2:获取用户 contactId
从 `user_index.json` 获取当前用户的 contactId 作为 namespace。
### 步骤 3:部署
```bash
bash scripts/deploy.sh <项目目录> <contactId> <项目名>
```
脚本自动执行:打包 → 上传 → 部署 → 返回 URL
### 步骤 4:返回结果
```
✅ 部署成功!
🌐 访问地址:https://<域名>/<contactId>/<目录名>/
📂 目录名:<随机ID>-<项目名>-<时间戳>
⏱️ 部署耗时:X 秒
```
---
## 远程命令执行(安装环境等)
```bash
bash scripts/exec.sh "<命令>" [超时毫秒]
```
**安全限制**:
- 危险命令会被拦截(rm -rf /、shutdown 等)
- 操作目录受限
- 每条命令都有审计日志
---
## API 接口一览
| 接口 | 方法 | 权限 | 用途 | 脚本 |
|------|------|------|------|------|
| /api/auth/register | POST | 注册码 | 自注册 | register.sh |
| /api/deploy | POST | deploy | 一键部署 | deploy.sh |
| /api/upload | POST | deploy | 上传文件 | upload.sh |
| /api/exec | POST | exec | 执行命令 | exec.sh |
| /api/deployments | GET | list | 查看部署列表 | list.sh |
| /api/deploy/{id} | DELETE | delete | 删除部署 | delete.sh |
| /api/health | GET | 无 | 健康检查 | health.sh |
---
## 脚本说明
| 脚本 | 功能 |
|------|------|
| `scripts/init-config.sh` | 初始化配置文件(环境变量驱动) |
| `scripts/register.sh` | Token 自注册 |
| `scripts/deploy.sh` | 主部署脚本(打包+上传+部署) |
| `scripts/pack.sh` | 打包项目为 tar.gz |
| `scripts/exec.sh` | 远程执行命令 |
| `scripts/upload.sh` | 上传文件到指定路径 |
| `scripts/list.sh` | 查看部署列表 |
| `scripts/delete.sh` | 删除指定部署 |
| `scripts/health.sh` | 健康检查 |
---
## 用户交互模式
### 模式 1:部署静态页面(最常用)
```
用户:部署这个静态页面
AI:🔍 检测到项目:landing-page(静态页面)
🔑 Token 已就绪
👥 Namespace:<contactId>
正在部署...
📦 打包文件中...(15 个文件,2.3MB)
🚀 上传并部署中...
✅ 部署成功!
🌐 访问地址:https://<域名>/<contactId>/<目录名>/
⏱️ 部署耗时:5 秒
```
### 模式 2:首次使用(自动注册)
```
用户:部署这个项目
AI:🔍 检测到首次使用云部署,正在初始化...
📝 配置文件已创建
🔑 注册成功,Token 已保存
继续部署...
(后续同模式 1)
```
### 模式 3:安装环境
```
用户:服务器上需要装 Python
AI:🔧 正在执行远程命令...
> sudo apt update && sudo apt install -y python3 python3-pip
✅ 执行完成(耗时 12 秒)
```
### 模式 4:查看部署列表
```
用户:看看已部署的项目
AI:📋 你的部署列表(共 N 个):
1. <目录名>
🌐 <URL>
⏰ <部署时间>
```
### 模式 5:删除部署
```
用户:删掉第一个项目
AI:🗑️ 已删除 <目录名>
文件已清理
```
---
## 安全设计
### Token 机制
| 要素 | 说明 |
|------|------|
| 注册码 | 技能配置时注入,只用于注册 |
| Token | 每个 AI 助理独立,可单独撤销 |
| 权限 | deploy / exec / list / delete |
| 审计 | 每次操作都记录到服务端日志 |
### 危险命令拦截
以下命令会被服务端拦截:
- rm -rf /(删除根目录)
- shutdown / reboot(关机重启)
- mkfs / dd if=(格式化磁盘)
- iptables -F / ufw disable(关闭防火墙)
- systemctl stop nginx(停止 Nginx)
- curl|bash / wget|bash(远程执行)
### 路径保护
/api/exec 和 /api/upload 只允许操作服务端配置的允许目录。
### 管理员操作(仅 owner)
| 操作 | 接口 |
|------|------|
| 查看所有 Token | GET /api/admin/tokens |
| 撤销 Token | DELETE /api/admin/tokens/{prefix} |
| 查看审计日志 | GET /api/admin/audit?date=YYYY-MM-DD |
---
## 服务端部署
API 网关服务端部署说明见 `references/server-setup.md`
---
## 目录结构
```
skills/cloud-deploy/
├── SKILL.md # 技能说明(本文件)
├── README.md # 快速入门
├── QUICKSTART.sh # 快速启动脚本
├── scripts/ # 客户端脚本
│ ├── init-config.sh
│ ├── register.sh
│ ├── deploy.sh
│ ├── pack.sh
│ ├── exec.sh
│ ├── upload.sh
│ ├── list.sh
│ ├── delete.sh
│ ├── health.sh
│ └── legacy/ # v1.0 旧脚本(保留备查)
├── references/ # 参考文档
│ ├── server-setup.md # 服务端部署指南
│ └── security-guide.md # 安全指南
└── assets/
└── templates/
```
---
_版本:v2.0 | 更新:2026-04-21(SSH → API 网关模式)_
_v1.0 备份:skills/cloud-deploy-v1.0-backup/_

View File

@ -0,0 +1,71 @@
# Nginx 静态页面配置模板
# 使用时替换以下变量:
# {PORT} - 端口号
# {DEPLOY_DIR} - 部署目录路径
# {IP} - 服务器 IP
server {
listen {PORT};
server_name {IP};
# 部署目录
root {DEPLOY_DIR};
index index.html index.htm;
# 日志配置
access_log /var/log/nginx/deploy_{PORT}_access.log;
error_log /var/log/nginx/deploy_{PORT}_error.log warn;
# 静态文件缓存优化
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|otf|woff)$ {
expires 30d;
add_header Cache-Control "public";
add_header X-Static-File "true";
}
# HTML 文件不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# SPA 路由支持(单页应用)
# 如果不需要可删除此配置
location / {
try_files $uri $uri/ /index.html;
}
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
gzip_disable "MSIE [1-6]\.";
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 禁止访问备份文件
location ~* \.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ {
deny all;
access_log off;
log_not_found off;
}
# 限制请求大小
client_max_body_size 10M;
# 连接优化
keepalive_timeout 65;
keepalive_requests 100;
}

View File

@ -0,0 +1,307 @@
# 安全配置指南
## SSH 安全
### 1. 密钥管理
**生成密钥**
```bash
ssh-keygen -t ed25519 -f ~/.ssh/aliyun_deploy_key -N "" -C "aliyun_deploy_key"
```
**密钥权限**
```bash
chmod 600 ~/.ssh/aliyun_deploy_key
chmod 644 ~/.ssh/aliyun_deploy_key.pub
```
**密钥轮换**(建议每 6 个月)
```bash
# 生成新密钥
ssh-keygen -t ed25519 -f ~/.ssh/aliyun_deploy_key_new -N ""
# 复制公钥到服务器
cat ~/.ssh/aliyun_deploy_key_new.pub | ssh root@IP "cat >> ~/.ssh/authorized_keys"
# 测试新密钥
ssh -i ~/.ssh/aliyun_deploy_key_new root@IP "exit"
# 成功后删除旧密钥
```
### 2. SSH 配置加固
编辑 `/etc/ssh/sshd_config`:
```bash
# 禁用密码登录
PasswordAuthentication no
# 禁用 root 登录(可选,创建普通用户)
PermitRootLogin prohibit-password
# 限制用户
AllowUsers root
# 更改 SSH 端口(可选)
Port 2222
# 限制登录 IP(可选)
AllowUsers root@1.2.3.4
# 空闲超时
ClientAliveInterval 300
ClientAliveCountMax 2
```
重启 SSH 服务:
```bash
systemctl restart sshd
```
## 防火墙配置
### 阿里云安全组
**必需端口**
| 端口 | 用途 | 授权对象 |
|------|------|---------|
| 22 | SSH | 建议限制 IP |
| 80 | HTTP | 0.0.0.0/0 |
| 443 | HTTPS | 0.0.0.0/0 |
| 8080-8090 | 应用 | 0.0.0.0/0 |
**可选加固**
- SSH 端口限制为特定 IP
- 启用安全组日志
### UFW 防火墙(Ubuntu)
```bash
# 安装 UFW
apt install -y ufw
# 默认拒绝入站
ufw default deny incoming
# 允许出站
ufw default allow outgoing
# 允许 SSH
ufw allow 22/tcp
# 允许 HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp
# 允许应用端口
ufw allow 8080:8090/tcp
# 启用 UFW
ufw enable
# 查看状态
ufw status verbose
```
## 文件权限
### 部署目录权限
```bash
# 部署目录
chmod 755 /opt/deployments
# 项目目录
chmod 755 /opt/deployments/<project-dir>
# 文件
chmod 644 /opt/deployments/<project-dir>/*
# 敏感文件(如.env)
chmod 600 /opt/deployments/<project-dir>/.env
```
### Nginx 配置权限
```bash
chmod 644 /etc/nginx/sites-available/*
chmod 755 /etc/nginx/sites-enabled/
```
## Nginx 安全配置
### 隐藏版本信息
```nginx
server_tokens off;
```
### 安全头
```nginx
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
```
### 限制请求大小
```nginx
client_max_body_size 10M;
```
### 限制请求频率
```nginx
# 在 http 块中
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# 在 server 块中
limit_req zone=one burst=20 nodelay;
```
## 日志审计
### 访问日志
```bash
# 查看实时访问
tail -f /var/log/nginx/access.log
# 查看访问最多的 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# 查看 404 错误
awk '$9 == 404 {print $0}' /var/log/nginx/access.log
```
### 错误日志
```bash
# 查看实时错误
tail -f /var/log/nginx/error.log
# 查看最近错误
tail -100 /var/log/nginx/error.log
```
### 日志轮转
```bash
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then
run-parts /etc/logrotate.d/httpd-prerotate
fi
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
```
## 定期维护
### 1. 系统更新
```bash
# 每周执行
apt update && apt upgrade -y
```
### 2. 日志清理
```bash
# 清理 30 天前的日志
find /var/log/nginx -name "*.log" -mtime +30 -delete
```
### 3. 磁盘清理
```bash
# 查找大文件
find /opt/deployments -type f -size +100M
# 清理临时文件
rm -rf /tmp/*
```
### 4. 检查异常连接
```bash
# 查看活跃连接
netstat -an | grep ESTABLISHED | wc -l
# 查看异常 IP
netstat -an | grep ESTABLISHED | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn
```
## 应急响应
### 1. 发现异常访问
```bash
# 立即封禁 IP
ufw deny from 1.2.3.4
# 或在 Nginx 中封禁
# /etc/nginx/conf.d/block.conf
deny 1.2.3.4;
```
### 2. 网站被篡改
```bash
# 1. 停止 Nginx
systemctl stop nginx
# 2. 备份现场
tar czf /tmp/deployments_backup_$(date +%Y%m%d_%H%M%S).tar.gz /opt/deployments
# 3. 从本地重新部署
# 运行部署脚本
# 4. 启动 Nginx
systemctl start nginx
```
### 3. SSH 被暴力破解
```bash
# 安装 fail2ban
apt install -y fail2ban
# 配置 SSH 保护
cat > /etc/fail2ban/jail.local <<EOF
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 3600
EOF
systemctl restart fail2ban
```
## 备份策略
### 1. 部署配置备份
```bash
# 备份 Nginx 配置
tar czf nginx_config_$(date +%Y%m%d).tar.gz /etc/nginx/
# 备份部署记录
cp ~/.openclaw/deployments.json ~/deployments_backup_$(date +%Y%m%d).json
```
### 2. 项目文件备份
```bash
# 备份所有部署
tar czf deployments_$(date +%Y%m%d).tar.gz /opt/deployments/
```
### 3. 自动化备份(cron)
```bash
# 每周日凌晨 2 点备份
0 2 * * 0 tar czf /backup/deployments_$(date +\%Y\%m\%d).tar.gz /opt/deployments/
```

View File

@ -0,0 +1,165 @@
# 服务端部署指南(API 网关)
> 本文档说明如何在云服务器上部署 Cloud Deploy API 网关服务
## 前置条件
- Ubuntu 20.04+ 服务器
- 域名已解析到服务器
- 80/443 端口可访问
## 部署步骤
### 1. 安装 Node.js
```bash
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
```
### 2. 安装 PM2
```bash
sudo npm install -g pm2
```
### 3. 创建服务目录
```bash
sudo mkdir -p /data/disk/deploy-api/{logs,tmp}
sudo mkdir -p /data/disk/deployments
sudo chown -R $USER:$USER /data/disk/deploy-api
sudo chown -R $USER:$USER /data/disk/deployments
```
### 4. 部署服务文件
将以下文件放到 `/data/disk/deploy-api/`:
- server.js — 主服务
- package.json — 依赖
- config.json — 服务配置
- tokens.json — Token 存储
- security.json — 安全规则
### 5. 配置 config.json
```json
{
"port": 3900,
"domain": "your-domain.com",
"deployDir": "/data/disk/deployments",
"dataDir": "/data/disk/deploy-api",
"maxUploadSize": "100mb",
"execTimeout": 300000,
"logRetainDays": 90
}
```
### 6. 配置 tokens.json
```json
{
"registrationKey": "<生成一个随机注册码>",
"tokens": {
"<生成一个admin token>": {
"agentId": "manual",
"instanceName": "管理员",
"contactId": "",
"permissions": ["deploy", "exec", "list", "delete", "admin"],
"active": true,
"createdAt": "<当前时间>",
"lastUsedAt": null
}
}
}
```
生成随机字符串:
```bash
# 注册码
echo "reg_$(openssl rand -hex 12)"
# Admin Token
echo "tok_admin_$(openssl rand -hex 16)"
```
### 7. 安装依赖并启动
```bash
cd /data/disk/deploy-api
npm install
pm2 start server.js --name deploy-api
pm2 save
pm2 startup
```
### 8. 配置 Nginx 反向代理
```nginx
server {
listen 80;
server_name your-domain.com;
root /data/disk/deployments;
index index.html index.htm;
# API 反向代理
location /api/ {
proxy_pass http://127.0.0.1:3900/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100M;
proxy_read_timeout 300s;
}
# 静态文件
location / {
try_files $uri $uri/ =404;
autoindex on;
}
}
```
### 9. 配置 HTTPS(可选)
```bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
```
### 10. 验证
```bash
curl https://your-domain.com/api/health
```
## 日常维护
### 查看日志
```bash
# PM2 日志
pm2 logs deploy-api
# 审计日志
cat /data/disk/deploy-api/logs/audit-$(date +%Y-%m-%d).jsonl | python3 -m json.tool
```
### 重启服务
```bash
pm2 restart deploy-api
```
### 更新代码
```bash
cd /data/disk/deploy-api
# 替换 server.js
pm2 restart deploy-api
```
---
_Cloud Deploy v2.0 服务端部署指南_

View File

@ -0,0 +1,34 @@
#!/bin/bash
# 删除指定部署
# 用法: bash delete.sh <deployId>
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
DEPLOY_ID="$1"
if [ -z "$DEPLOY_ID" ]; then
echo "用法: bash delete.sh <deployId>"
exit 1
fi
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
RESPONSE=$(curl -s --max-time 10 \
-X DELETE \
-H "Authorization: Bearer $TOKEN" \
"$API_URL/deploy/$DEPLOY_ID" 2>/dev/null)
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
DELETED=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('deleted',''))" 2>/dev/null)
echo "✅ 已删除: $DELETED"
else
echo "❌ 删除失败"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
fi

View File

@ -0,0 +1,92 @@
#!/bin/bash
# 主部署脚本:打包 + 上传 + 部署
# 用法: bash deploy.sh <项目目录> <contactId> <项目名>
set -e
SCRIPT_DIR="$(dirname "$0")"
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
PROJECT_DIR="$1"
CONTACT_ID="$2"
PROJECT_NAME="$3"
if [ -z "$PROJECT_DIR" ] || [ -z "$CONTACT_ID" ] || [ -z "$PROJECT_NAME" ]; then
echo "用法: bash deploy.sh <项目目录> <contactId> <项目名>"
exit 1
fi
if [ ! -d "$PROJECT_DIR" ]; then
echo "错误:项目目录不存在: $PROJECT_DIR"
exit 1
fi
# 首次使用自动初始化配置
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 确保已注册
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
if [ -z "$TOKEN" ]; then
echo "🔑 首次使用,正在注册..."
bash "$SCRIPT_DIR/register.sh" "$CONTACT_ID"
TOKEN=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['token'])" 2>/dev/null)
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
# 打包
echo "📦 打包项目..."
PACK_OUTPUT=$(bash "$SCRIPT_DIR/pack.sh" "$PROJECT_DIR" "$PROJECT_NAME")
TAR_FILE=$(echo "$PACK_OUTPUT" | tail -1)
if [ ! -f "$TAR_FILE" ]; then
echo "错误:打包失败"
exit 1
fi
# 部署
echo "🚀 上传并部署中..."
START_TIME=$(date +%s)
RESPONSE=$(curl -s --max-time 120 \
-X POST "$API_URL/deploy" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@$TAR_FILE" \
-F "contactId=$CONTACT_ID" \
-F "projectName=$PROJECT_NAME" 2>/dev/null)
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# 清理临时文件
rm -f "$TAR_FILE"
if [ -z "$RESPONSE" ]; then
echo "❌ API 无响应"
exit 1
fi
# 检查结果
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
URL=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('url',''))" 2>/dev/null)
DIR_NAME=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('dirName',''))" 2>/dev/null)
DEPLOY_ID=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null)
echo ""
echo "✅ 部署成功!"
echo ""
echo "🌐 访问地址:$URL"
echo "📂 目录名:$DIR_NAME"
echo "🆔 部署ID:$DEPLOY_ID"
echo "⏱️ 部署耗时:${DURATION} 秒"
else
ERROR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error','unknown'))" 2>/dev/null)
MSG=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo "❌ 部署失败: $ERROR - $MSG"
exit 1
fi

View File

@ -0,0 +1,82 @@
#!/bin/bash
# 远程执行命令
# 用法: bash exec.sh "<命令>" [超时毫秒]
set -e
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
SCRIPT_DIR="$(dirname "$0")"
COMMAND="$1"
TIMEOUT="${2:-120000}"
if [ -z "$COMMAND" ]; then
echo "用法: bash exec.sh \"<命令>\" [超时毫秒]"
exit 1
fi
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 确保已注册
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
if [ -z "$TOKEN" ]; then
echo "🔑 首次使用,正在注册..."
bash "$SCRIPT_DIR/register.sh"
TOKEN=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['token'])" 2>/dev/null)
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
echo "🔧 执行远程命令..."
echo " > $COMMAND"
# 用 python3 安全转义命令字符串
CMD_JSON=$(python3 -c "import json; print(json.dumps('$COMMAND'))")
RESPONSE=$(curl -s --max-time 300 \
-X POST "$API_URL/exec" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"command\": $CMD_JSON,
\"timeout\": $TIMEOUT
}" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "❌ API 无响应"
exit 1
fi
# 检查是否被拦截
BLOCKED=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error', ''))" 2>/dev/null)
if [ "$BLOCKED" = "blocked" ]; then
RULE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('rule', ''))" 2>/dev/null)
echo "🚫 命令被安全策略拦截"
echo " 规则: $RULE"
exit 1
fi
EXIT_CODE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('exitCode', -1))" 2>/dev/null)
STDOUT=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('stdout', ''))" 2>/dev/null)
STDERR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('stderr', ''))" 2>/dev/null)
DURATION=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('durationMs', 0))" 2>/dev/null)
echo ""
if [ "$EXIT_CODE" = "0" ]; then
echo "✅ 执行成功(耗时 ${DURATION}ms)"
else
echo "⚠️ 执行完成,退出码: $EXIT_CODE(耗时 ${DURATION}ms)"
fi
if [ -n "$STDOUT" ]; then
echo "--- stdout ---"
echo "$STDOUT"
fi
if [ -n "$STDERR" ]; then
echo "--- stderr ---"
echo "$STDERR"
fi

View File

@ -0,0 +1,21 @@
#!/bin/bash
# 健康检查
# 用法: bash health.sh
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
if [ ! -f "$CONFIG_FILE" ]; then
echo "配置文件不存在,正在初始化..."
bash "$(dirname "$0")/init-config.sh"
fi
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
RESPONSE=$(curl -s --max-time 10 "$API_URL/health" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "❌ API 不可达: $API_URL"
exit 1
fi
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"

View File

@ -0,0 +1,39 @@
#!/bin/bash
# 初始化 cloud-deploy 本地配置
#
# 预配置版本(阿创分发专用)
# 新 AI 助手无需手动输入参数,直接可用
#
# 如需修改 API 地址或注册码,直接编辑本脚本中的默认值
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
if [ -f "$CONFIG_FILE" ]; then
echo "配置文件已存在: $CONFIG_FILE"
cat "$CONFIG_FILE"
exit 0
fi
# ============================================================
# 预配置默认值(部署时按需修改)
# ============================================================
API_URL="https://openclawdev.lovebenefits.cn/api"
REG_KEY="regOC2026deployX7k9m2p4"
# ============================================================
mkdir -p "$(dirname "$CONFIG_FILE")"
python3 -c "
import json
config = {
'apiUrl': '$API_URL',
'registrationKey': '$REG_KEY',
'token': None,
'agentId': ''
}
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
echo "配置文件已创建: $CONFIG_FILE"
cat "$CONFIG_FILE"

View File

@ -0,0 +1,25 @@
#!/bin/bash
# 查看部署列表
# 用法: bash list.sh [contactId]
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
CONTACT_ID="$1"
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
URL="$API_URL/deployments"
if [ -n "$CONTACT_ID" ]; then
URL="$URL?contactId=$CONTACT_ID"
fi
RESPONSE=$(curl -s --max-time 10 \
-H "Authorization: Bearer $TOKEN" \
"$URL" 2>/dev/null)
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"

View File

@ -0,0 +1,42 @@
#!/bin/bash
# 打包项目为 tar.gz
# 用法: bash pack.sh <项目目录> <项目名>
set -e
PROJECT_DIR="$1"
PROJECT_NAME="$2"
if [ -z "$PROJECT_DIR" ] || [ -z "$PROJECT_NAME" ]; then
echo "用法: bash pack.sh <项目目录> <项目名>"
exit 1
fi
if [ ! -d "$PROJECT_DIR" ]; then
echo "错误:项目目录不存在: $PROJECT_DIR"
exit 1
fi
# 输出文件
OUTPUT="/tmp/cloud-deploy-${PROJECT_NAME}-$(date +%s).tar.gz"
# 打包(排除无关文件)
tar -czf "$OUTPUT" \
--exclude='node_modules' \
--exclude='.git' \
--exclude='.env' \
--exclude='*.log' \
--exclude='.DS_Store' \
--exclude='__pycache__' \
--exclude='.pytest_cache' \
--exclude='*.pyc' \
-C "$PROJECT_DIR" .
FILE_SIZE=$(du -h "$OUTPUT" | cut -f1)
FILE_COUNT=$(tar -tzf "$OUTPUT" | wc -l)
echo "✅ 打包完成"
echo " 文件: $OUTPUT"
echo " 大小: $FILE_SIZE"
echo " 文件数: $FILE_COUNT"
echo "$OUTPUT"

View File

@ -0,0 +1,103 @@
#!/bin/bash
# Token 自注册脚本
# 首次使用时自动调用,获取独立 Token
#
# 前置条件:~/.openclaw/cloud-deploy.json 已初始化(含 apiUrl + registrationKey)
# 用法: bash register.sh [contactId]
set -e
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
SCRIPT_DIR="$(dirname "$0")"
# 检查配置文件
if [ ! -f "$CONFIG_FILE" ]; then
echo "📝 首次使用,自动初始化配置..."
bash "$SCRIPT_DIR/init-config.sh"
fi
# 读取配置
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
REG_KEY=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['registrationKey'])")
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')")
# 如果已有 Token,验证是否有效
if [ -n "$TOKEN" ]; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
-H "Authorization: Bearer $TOKEN" \
"$API_URL/health" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" != "000" ]; then
echo "Token 已存在且有效"
echo "$TOKEN"
exit 0
fi
fi
# 获取 agentId(配置文件 → hostname 兜底)
AGENT_ID=$(python3 -c "import json; a=json.load(open('$CONFIG_FILE')).get('agentId',''); print(a if a else '')" 2>/dev/null)
if [ -z "$AGENT_ID" ]; then
AGENT_ID="$(hostname)-$(date +%s)"
fi
INSTANCE_NAME="$(hostname)"
CONTACT_ID="${1:-unknown}"
echo "正在注册 Token..."
echo " API: $API_URL"
echo " AgentId: $AGENT_ID"
echo " Instance: $INSTANCE_NAME"
# 调用注册接口
RESPONSE=$(curl -s --max-time 15 \
-X POST "$API_URL/auth/register" \
-H "Content-Type: application/json" \
-d "{
\"registrationKey\": \"$REG_KEY\",
\"agentId\": \"$AGENT_ID\",
\"instanceName\": \"$INSTANCE_NAME\",
\"contactId\": \"$CONTACT_ID\"
}" 2>/dev/null)
if [ -z "$RESPONSE" ]; then
echo "错误:API 无响应"
exit 1
fi
# 检查是否有错误
ERROR=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('error',''))" 2>/dev/null)
if [ -n "$ERROR" ]; then
MSG=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo "注册失败: $ERROR - $MSG"
exit 1
fi
# 提取 Token
NEW_TOKEN=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('token',''))" 2>/dev/null)
if [ -z "$NEW_TOKEN" ]; then
echo "错误:未获取到 Token"
echo "$RESPONSE"
exit 1
fi
# 保存 Token 到配置
python3 -c "
import json
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
config['token'] = '$NEW_TOKEN'
config['agentId'] = '$AGENT_ID'
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
# 注册成功后清除本地注册码(已完成使命,避免泄露)
python3 -c "
import json
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
config['registrationKey'] = ''
with open('$CONFIG_FILE', 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
"
echo "✅ 注册成功,Token 已保存,注册码已清除"
echo "$NEW_TOKEN"

View File

@ -0,0 +1,41 @@
#!/bin/bash
# 上传文件到服务器指定路径
# 用法: bash upload.sh <本地文件> <目标路径>
CONFIG_FILE="$HOME/.openclaw/cloud-deploy.json"
LOCAL_FILE="$1"
TARGET_PATH="$2"
if [ -z "$LOCAL_FILE" ] || [ -z "$TARGET_PATH" ]; then
echo "用法: bash upload.sh <本地文件> <目标路径>"
exit 1
fi
if [ ! -f "$LOCAL_FILE" ]; then
echo "错误:文件不存在: $LOCAL_FILE"
exit 1
fi
TOKEN=$(python3 -c "import json; t=json.load(open('$CONFIG_FILE')).get('token'); print(t if t else '')" 2>/dev/null)
API_URL=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['apiUrl'])")
if [ -z "$TOKEN" ]; then
echo "错误:未注册,请先执行 register.sh"
exit 1
fi
RESPONSE=$(curl -s --max-time 60 \
-X POST "$API_URL/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@$LOCAL_FILE" \
-F "targetPath=$TARGET_PATH" 2>/dev/null)
SUCCESS=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('success', False))" 2>/dev/null)
if [ "$SUCCESS" = "True" ]; then
SIZE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('size', 0))" 2>/dev/null)
echo "✅ 上传成功: $TARGET_PATH ($SIZE bytes)"
else
echo "❌ 上传失败"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
fi