Renderizado do repositório de origem, preservando títulos, exemplos, código, tabelas, links e imagens.
UCloud 站点空间
使用 ucloud-sandbox-cli 操作用户已经创建的站点空间。站点空间底层是一个沙箱;本技能只负责连接和维护该站点,不创建站点或沙箱。
前置检查和平台
执行 CLI 安装或调用站点 API 前,先确认当前环境允许访问公网。需要网络权限审批时先申请授权;未获授权时停止并说明原因。
在 Windows 或 PowerShell 环境中,执行安装、连接、文件传输或部署前,先完整阅读并遵循 Windows PowerShell 指南。本页的 Bash 安装、凭证注入和本地打包命令仅适用于 Linux 和 macOS;传给 sandbox exec 的命令仍在远端 Linux Shell 中执行。
核心概念
- 站点连接 Key 格式为
site_<sandbox-id>_<code>,<code>是站点空间随机生成的连接码,用于防止他人仅凭沙箱 ID 就连接到站点。例如site_iy1qen6gs2835o0udufdz_7f3a9c2e对应沙箱 IDiy1qen6gs2835o0udufdz。 - 沙箱 ID 是
site_前缀之后、第一个_之前的部分。连接码只是 Key 的组成部分,不要单独传给 CLI,也不要拼进沙箱 ID。 - 站点连接 Key 同时是一个受限 API Key。把完整 Key 写入
UCLOUD_SANDBOX_API_KEY,但向 CLI 传资源 ID 时只使用派生出的沙箱 ID。 - 旧格式
site_<sandbox-id>(不带_<code>)已不再可用。用户提供旧格式时,不要尝试补全、猜测或省略连接码,也不要改用普通 API Key,直接请用户到星图控制台的站点空间页面重新复制新的站点连接语句。 - 站点凭证只能操作它绑定的一个沙箱,不能列出或删除沙箱,也不能操作模板等其他资源。
/home/user/.site.env保存用户为网站配置的环境变量。它由站点空间管理,不要删除、覆盖或纳入部署包。- 沙箱默认包含 Python 和 Node.js;确有需要时,可以通过
sandbox exec调用apt安装其他依赖。 - 沙箱命令默认以
user用户运行。user已配置免密 sudo;绑定 80 端口以及其他需要 root 权限的操作要直接使用非交互的sudo -n,不要先以普通用户试运行。
把站点连接 Key 视为凭证:不要在回复、日志、生成的代码或仓库文件中重复暴露它(包括其中的连接码),也不要写入 ~/.ucloud-sandbox-cli/config.json。仅在执行 CLI 的 shell 环境中临时设置。派生出的沙箱 ID 不含连接码,可以正常出现在命令中。
权限和安全边界
站点操作只使用以下能力:
sandbox exec:执行命令、检查环境、构建项目和管理服务。sandbox host:获取 80 端口的站点访问地址。fs ls、fs cat、fs mkdir、fs cp、fs mv、fs rm:管理该站点沙箱内的文件和目录。
不要尝试 sandbox list、create、clone、kill、pause,也不要执行快照、模板或其他管理命令。站点凭证返回无权限并不表示站点连接失败。
执行文件删除、覆盖或大范围移动前,先确认路径属于当前网站且操作符合用户意图。不要为了“清理部署目录”删除 /home/user、/home/user/.site.env 或来源不明的已有文件。
准备并验证 CLI
最低支持版本是 v1.3.2。v1.3.1 开始让控制面、RPC、文件和流式请求统一继承 HTTP_PROXY、HTTPS_PROXY 与 NO_PROXY;v1.3.2 进一步要求官方安装器校验 Release SHA256。更早版本在受限网络中可能误报 DNS 失败,或缺少安装完整性校验。
先执行一次 ucloud-sandbox-cli version。AstraFlow 的本地 Agent PATH 包含真实用户的通用 CLI 目录(Linux/macOS 为 ~/.local/bin),不是每个会话的私有 $HOME,也不是 AstraFlow 的 Application Support 目录。已满足最低版本时直接继续,不要更新。
如果命令不存在或低于 v1.3.2,只把官方安装命令安装到真实用户的 ~/.local/bin。这一步必须作为一个完整命令申请一次主机执行权限;获批后不要再为安装器内部的下载、临时目录或写文件分别申请权限:
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/ucloud-sandbox-cli.XXXXXXXX")" && \
trap 'rm -rf "$TMP_DIR"' EXIT && \
curl -fsSLo "$TMP_DIR/install.sh" \
https://raw.githubusercontent.com/ucloud/ucloud-sandbox-cli/main/install.sh && \
sh "$TMP_DIR/install.sh" -y -p "$HOME/.local/bin" && \
"$HOME/.local/bin/ucloud-sandbox-cli" version不要先尝试 /usr/local/bin,不要使用 sudo,不要安装到 Agent 会话的私有 $HOME,不要写入或硬编码 AstraFlow 的 sandbox-workspaces / Application Support 路径,也不要用 find $HOME 搜索 CLI。安装完成后回到 Default 沙箱,按命令名 ucloud-sandbox-cli 使用;真实用户目录跨会话和重启复用。
Windows 按 Windows PowerShell 指南 使用用户级安装目录;同样把安装与版本验证合并成一次获批的主机命令。
仅当用户明确要求更新 CLI 时,如果已安装的版本是 v1.3.4 或更高,直接用自带的 update 命令,不必重新下载安装脚本。这里没有交互式终端,必须带 -y 跳过确认:
ucloud-sandbox-cli update -y && ucloud-sandbox-cli versionupdate 会校验 Release SHA256 后替换当前程序,保持原安装目录不变。低于 v1.3.4 的版本没有该命令,按上面的安装流程重装。查询版本报 GitHub API 限流时,说明原因并让用户稍后重试或设置 GITHUB_TOKEN。
本节只准备 ucloud-sandbox-cli,不要安装或更新本 Skill 本身。
连接站点
1. 获取并校验站点连接 Key
如果用户还没有提供站点连接 Key,只向用户索取 site_<sandbox-id>_<code> 格式的连接 Key(即站点空间页面上的站点连接语句);不要索取普通 UCloud API Key,也不要只要沙箱 ID。先按照上一节完成 CLI 安装和验证。
Linux 和 macOS 在同一个 shell 调用中校验 Key、设置凭证并派生沙箱 ID:
SITE_KEY='site_<sandbox-id>_<code>'
case "$SITE_KEY" in
site_*_*) ;;
site_?*)
echo "检测到旧版站点 ID 格式 site_<sandbox-id>,已不再支持。请到星图控制台的站点空间页面重新获取站点连接语句(site_<sandbox-id>_<code>)。" >&2
exit 1 ;;
*)
echo "站点连接 Key 格式无效,应为 site_<sandbox-id>_<code>" >&2
exit 1 ;;
esac
SITE_BODY="${SITE_KEY#site_}"
SANDBOX_ID="${SITE_BODY%%_*}"
SITE_CODE="${SITE_BODY#*_}"
if [ -z "$SANDBOX_ID" ] || [ -z "$SITE_CODE" ]; then
echo "站点连接 Key 缺少沙箱 ID 或连接码,请到星图控制台的站点空间页面重新获取站点连接语句。" >&2
exit 1
fi
export UCLOUD_SANDBOX_API_KEY="$SITE_KEY"SANDBOX_ID 只取第一个 _ 之前的部分,连接码留在 UCLOUD_SANDBOX_API_KEY 中;不要把 SITE_CODE 或完整 Key 传给任何 CLI 子命令的资源 ID 参数。
校验失败时立即停止,不要继续尝试 sandbox exec。用户提供旧格式时,只回复需要到站点空间页面重新获取连接语句,不要输出用户给出的旧 ID 之外的推测值。
环境变量只对当前 shell 进程及其子进程有效。Agent 每次开启新的 shell 调用时,都要重新注入 UCLOUD_SANDBOX_API_KEY 并派生 SANDBOX_ID,不要假设上一次 export 仍然有效。
站点所在地域或 API 域名仍由已有 CLI 配置以及 UCLOUD_SANDBOX_REGION、UCLOUD_SANDBOX_DOMAIN 决定。没有证据时不要擅自切换;连接失败且怀疑地域不符时,向用户确认站点地域。
2. 在一次调用中验证连接并检查初始目录
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" \
"printf 'SITE_CONNECTED\\n'; pwd; printf 'SITE_HOME\\n'; ls -la /home/user"只要这个 exec 成功,就认为 AI 已连接站点并已完成首次目录检查,可以继续生成代码和部署。不要把连接验证、pwd 和初始 ls 拆成多个调用,也不要用 sandbox list 验证连接。
3. 识别站点访问码保护
CLI 连接验证与公共 URL 访问是两件事:AI 可以通过 CLI 连接沙箱,但配置了访问码的公共站点不能被 AI 直接验证。
访问 sandbox host 或用户自定义域名时,使用不携带任何访问码的请求检查 HTTP 状态和响应体。若返回 401 且响应体包含 Site protected,判定为站点已开启访问码保护,而不是服务故障。不要猜测、绕过或重试访问码,也不要要求客户把访问码发送给 AI;访问码是敏感内容,不得写入命令、日志、代码或回复。
此时可以保留站点内 http://127.0.0.1:80 的验证结果,但公共页面验证必须交给客户:把 sandbox host 返回的地址交给客户,让客户在浏览器中自行输入访问码。向客户说明 AI 无法在访问码开启时完成公共页面验证。
如果客户确实需要 AI 在开发阶段验证公共页面,请客户先在站点空间控制台临时关闭访问码,完成验证后再重新开启。配置方法参考站点空间文档。即使客户主动提供访问码,也不要让 AI 代为使用或保存访问码。
4. 识别公共访问被拒绝(403)
公共 URL 返回 403 表示站点网关拒绝了这次访问,不是网站服务故障,也不是 CLI 连接失败。判定前先读取响应体,按下面三种情况区分处理,不要重试、绕过或更换出口 IP:
| 响应体特征 | 原因 | 处理 |
|---|---|---|
包含 This site does not accept requests from your IP address | 访问来源 IP 不被允许 | 请客户到星图控制台的站点空间页面检查该站点的 IP 白名单和黑名单配置,确认当前访问来源是否被放行 |
包含 Domain not allowed | 站点配置了自定义域名,只允许通过该域名访问 | CLI 无法获取站点配置的自定义域名,AI 不要猜测或拼接域名;把公共页面验证交给客户,请客户用自己在星图控制台配置的自定义域名访问 |
包含 Port not allowed | 访问了 80 以外的端口 | 站点只开放 80 端口。改回 80 端口访问,并确认服务监听 0.0.0.0:80,不要通过其他端口对外交付 |
IP 名单和自定义域名都属于站点侧配置,AI 无法通过 CLI 查询或修改,只能引导客户在星图控制台调整或改用自定义域名访问。此时可以保留站点内 http://127.0.0.1:80 的验证结果,但不要宣布公共页面验证通过。
常用命令
以下示例均假定当前 shell 已正确设置 UCLOUD_SANDBOX_API_KEY 和 SANDBOX_ID。sandbox 可以缩写为 sbx。
执行命令
命令内容必须作为一个完整字符串传给 exec:
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" "pwd && ls -la /home/user"
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" "python3 --version && node --version && npm --version"普通项目操作不要使用 sudo,包括上传或修改 /home/user 下的代码、安装项目依赖、执行构建和读取 .site.env。否则会产生 root 所有的项目文件,妨碍后续更新。
以下操作通常需要 root 权限,首次执行就使用 sudo -n:
- 监听 80 等小于 1024 的特权端口,以及停止由 root 启动的服务进程。
- 使用
apt-get安装系统依赖。 - 使用
systemctl、service或修改系统级服务配置。 - 写入
/etc、/usr、/usr/local、/var等系统目录,或调整不属于user的文件权限和所有者。
不要在本地对整个 ucloud-sandbox-cli 命令使用 sudo;只在传给 sandbox exec 的远端命令中提升确实需要的部分。不要使用交互式 sudo,需要提权时先用 sudo -n true 验证免密权限;失败则报告问题,不要等待密码输入。
仅在项目确实需要额外系统包时安装:
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" "sudo -n apt-get update && sudo -n apt-get install -y <package>"浏览和读取文件
ucloud-sandbox-cli fs ls "$SANDBOX_ID" /home/user
ucloud-sandbox-cli fs ls "$SANDBOX_ID" /home/user --format json
ucloud-sandbox-cli fs cat "$SANDBOX_ID" /home/user/site/index.html只对确认不含敏感信息的普通文件使用 fs cat。不要对 /home/user/.site.env 使用 fs cat。
创建目录
ucloud-sandbox-cli fs mkdir "$SANDBOX_ID" /home/user/site目录已存在时命令仍然成功,并提示 Directory already exists。创建多层目录时,从已有的父目录开始逐层调用 fs mkdir;如果需要一次创建完整目录树,可以通过 sandbox exec 执行经过校验的 mkdir -p。
上传和下载文件
fs cp 的远端端点格式是 <sandbox-id>:<path>,源和目标中必须恰好有一个远端端点。它一次只复制一个文件:
# 上传
ucloud-sandbox-cli fs cp ./index.html "$SANDBOX_ID:/home/user/site/index.html"
# 下载
ucloud-sandbox-cli fs cp "$SANDBOX_ID:/home/user/site/service.log" ./service.log上传目录时,先在本地打包,再上传并在站点中解压。排除 .env、凭证、依赖目录和其他不应部署的本地文件:
LOCAL_PROJECT_DIR='./site'
tar \
--exclude='.git' \
--exclude='.env' \
--exclude='.env.*' \
--exclude='node_modules' \
-czf /tmp/site-release.tgz -C "$LOCAL_PROJECT_DIR" .
ucloud-sandbox-cli fs cp /tmp/site-release.tgz "$SANDBOX_ID:/tmp/site-release.tgz"
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" \
"mkdir -p /home/user/site && tar -xzf /tmp/site-release.tgz -C /home/user/site && rm -f /tmp/site-release.tgz"上传前先检查目标目录。若已有网站,优先使用独立发布目录或只覆盖本次变更的文件,避免旧文件与新构建产物混杂,也不要无条件清空目录。
移动和删除文件
ucloud-sandbox-cli fs mv "$SANDBOX_ID" /home/user/site/old.html /home/user/site/index.html
ucloud-sandbox-cli fs rm "$SANDBOX_ID" /home/user/site/obsolete.htmlfs rm 是破坏性操作。执行前确认准确路径;需要删除目录或批量文件时,不要把未校验的用户输入拼入 rm -rf。
安全读取站点环境变量
用户询问已配置的环境变量时,可以告知变量是否存在,并展示非敏感变量。变量名只要不区分大小写地包含 API_KEY 或 KEY,就绝不能输出它的值;只说明该变量存在或显示 <已隐藏>。
不要直接运行以下可能泄露凭证的命令:
fs cat ... /home/user/.site.envcat /home/user/.site.env- 未过滤的
env、printenv、set或export -p - 在加载环境变量时启用
set -x
需要查看配置摘要时,在沙箱内逐行解析并脱敏:
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" 'awk '\''
/^[[:space:]]*(#|$)/ { next }
{
line=$0
sub(/^[[:space:]]*export[[:space:]]+/, "", line)
eq=index(line, "=")
if (!eq) next
name=substr(line, 1, eq-1)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", name)
if (name !~ /^[A-Za-z_][A-Za-z0-9_]*$/) next
if (toupper(name) ~ /API_KEY|KEY/) print name "=<已隐藏>"
else print line
}
'\'' /home/user/.site.env'如果只需确认某个敏感变量是否存在,验证变量名是合法 shell 标识符后,仅返回“存在”或“不存在”,不要返回值。若 .site.env 不存在,报告事实并询问用户,不要自行创建空文件替代它。
构建或启动用户网站时,通过下面的模式加载全部变量,不打印内容:
set -a
source /home/user/.site.env
set +a生成和部署网站
根据用户需求选择合适的前端技术栈。先浏览现有文件和项目配置,再在本地生成或修改代码并上传;不要无故替换用户已有框架。部署时遵守以下硬性要求:
- 服务必须监听
0.0.0.0:80,不能只监听127.0.0.1,也不能改用 3000、5173、8080 等端口交付。沙箱默认用户是user,绑定 80 端口时必须从第一次启动就使用sudo -n。 - 构建和启动服务时都要
source /home/user/.site.env;加载过程不得输出变量内容。 - 服务必须脱离
sandbox exec持久运行。使用nohup或站点中已有的服务管理器,并重定向标准输入、标准输出和标准错误。 - 保存 PID 和日志。root 服务的存活检查与停止也要使用
sudo -n kill;重启时只终止自己记录的旧 PID,不使用宽泛的pkill node、killall等命令影响其他进程。 - 只有在站点内访问
http://127.0.0.1:80成功后,才调用sandbox host并向用户宣布部署成功。
构建示例
Node 项目按项目自己的锁文件和脚本构建;下面仅是常见模式:
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" \
"cd /home/user/site && set -a && source /home/user/.site.env && set +a && npm ci && npm run build"如果没有 package-lock.json,按项目实际包管理器和锁文件选择命令,不要机械执行 npm ci。
不要用 sudo 执行 npm、pnpm、yarn 或构建脚本;只在启动最终的 80 端口服务时提权。
持久启动示例
对于构建产物位于 /home/user/site/dist 的纯静态网站,可以使用已安装的 Python 持久运行:
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" '
set -e
PID_FILE=/home/user/site/service.pid
LOG_FILE=/home/user/site/service.log
sudo -n true
if [ -s "$PID_FILE" ]; then
old_pid=$(cat "$PID_FILE")
if sudo -n kill -0 "$old_pid" 2>/dev/null; then
sudo -n kill "$old_pid"
fi
fi
sudo -n touch "$PID_FILE" "$LOG_FILE"
sudo -n chown user:user "$PID_FILE" "$LOG_FILE"
sudo -n bash -lc "
set -e
set -a
source /home/user/.site.env
set +a
nohup python3 -m http.server 80 --bind 0.0.0.0 --directory /home/user/site/dist \
>/home/user/site/service.log 2>&1 </dev/null &
echo \$! >/home/user/site/service.pid
"
'启动前通过 sudo 创建日志和 PID 文件,再把所有者归还给 user,既能兼容先前试错留下的 root 文件,也能避免后续维护需要一直提权。必须在 root 启动器内部记录 $!,确保 PID 指向真正的网站服务,而不是外层 sudo 包装进程。
对于 SSR、Node 或其他动态服务,保留同样的 sudo -n、PID、日志、后台运行和环境变量加载模式,把 exec python3 ... 替换成项目的生产启动命令,并显式设置或传入 HOST=0.0.0.0、PORT=80。不要把开发服务器当作默认的生产部署方案。
验证并返回地址
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" \
"curl -fsS --max-time 10 http://127.0.0.1:80/ >/dev/null && echo SITE_HTTP_OK"
ucloud-sandbox-cli sandbox host "$SANDBOX_ID" 80把 sandbox host 输出的实际地址原样告知用户,不要猜测或拼接域名。同时简要说明已部署内容、监听端口和验证结果。
验证公共 URL 时同时读取 HTTP 状态码和响应体,先区分网关拒绝和服务故障,再决定是否排查服务:
401且响应体包含Site protected:访问码保护,按“识别站点访问码保护”处理。403:访问被网关拒绝,按“识别公共访问被拒绝(403)”对照响应体判断是 IP 名单、自定义域名还是端口限制。
这两类情况都不表示网站服务异常,也不影响站点内 HTTP 验证结果,但都不能据此宣布公共页面验证成功。若验证失败且不属于上述两类,先检查进程、80 端口和服务日志:
ucloud-sandbox-cli sandbox exec "$SANDBOX_ID" \
'if [ -f /home/user/site/service.pid ]; then pid=$(cat /home/user/site/service.pid); sudo -n ps -p "$pid" -o pid=,stat=,cmd=; fi; sudo -n ss -ltnp | grep '\'':80'\'' || true; tail -n 100 /home/user/site/service.log'上例用单引号包住远端命令,使 $() 在沙箱中展开。改写命令或改变引号层级时,不要让本地 shell 提前执行远端表达式。
故障处理
| 现象 | 处理 |
|---|---|
用户提供的是旧格式 site_<sandbox-id> | 不要尝试连接或补全连接码;告知格式已更新,请用户到星图控制台的站点空间页面重新复制站点连接语句 |
exec 提示无权限或鉴权失败 | 确认完整 site_<sandbox-id>_<code> 被用作 API Key(连接码没有被截断),派生出的沙箱 ID 被用作资源 ID;仍失败时请用户确认连接语句是否已在控制台重新生成;不要改用 sandbox list 测试 |
| 提示找不到沙箱 | 检查是否误把完整连接 Key 或带连接码的字符串当成沙箱 ID,沙箱 ID 只取第一个 _ 之前的部分,并向用户确认站点地域 |
.site.env 不存在 | 报告缺失并询问用户;不要擅自创建或用本地 .env 覆盖 |
启动 80 端口时报 Permission denied | 确认启动命令从第一次执行就使用 sudo -n,不要先用普通用户尝试绑定 80 端口 |
sudo 等待密码或提示需要终端 | 改用 sudo -n;若 sudo -n true 失败,报告免密 sudo 配置异常,不要尝试输入密码 |
exec 在启动服务后不返回 | 确认服务已后台运行,并把 stdin、stdout、stderr 全部重定向 |
| 站点内访问 80 端口失败 | 检查 PID、日志、启动命令和监听地址,确认服务监听 0.0.0.0:80 |
公共 URL 返回 401 且响应体包含 Site protected | 这是访问码保护,不是服务故障;不要索取或使用访问码,交给客户自行输入验证;如需 AI 验证,先让客户临时关闭访问码 |
公共 URL 返回 403 且响应体包含 This site does not accept requests from your IP address | 访问来源 IP 被拒绝;请客户在星图控制台的站点空间页面检查该站点的 IP 白名单和黑名单配置,不要重试或更换出口 IP |
公共 URL 返回 403 且响应体包含 Domain not allowed | 站点配置了自定义域名,只能通过该域名访问;CLI 无法获取该域名,不要猜测或拼接,请客户用自己配置的自定义域名访问并验证 |
公共 URL 返回 403 且响应体包含 Port not allowed | 站点只开放 80 端口;改回 80 端口访问,并确认服务监听 0.0.0.0:80 |
sandbox host 有输出但页面打不开 | 不要据此宣布成功;先在站点内用 curl 验证,再检查进程和日志 |
CLI 低于 v1.3.2 且出现 DNS/代理错误 | 按“准备并验证 CLI”只申请一次主机安装命令,将 CLI 升级到真实用户目录;回到 Default 沙箱重试一次,不要切到主机执行站点命令 |
CLI 已是 v1.3.2 或更高但仍出现 DNS/代理错误 | 保持 Default 沙箱;如平台要求,只申请一次当前会话的网络授权并重试一次。仍失败则报告域名、端口和原始错误,不要继续申请主机执行权限 |

