tripocli v2.0.0:Tripo AI 3D 生成与加工 CLI(面向 Agent 与开发者,纯 Python 标准库、零第三方依赖)

覆盖文生 3D、图生 3D、多视图、批量生成,以及重拓扑、绑定、动画、智能分割、
纹理、PBR、智能 UV、部件补全、格式导出与下载。

代码组织:一个功能一个模块(tripo/features/),入口薄壳 tripocli.py。
This commit is contained in:
Mrpzx committed 2026-10-13 19:22:54 +08:00
commit 977f97995a
60 files changed
+5622

No files matched your search

+168
View File
@@ -0,0 +1,168 @@
"""argparse wiring and dispatch.
``tripocli`` is a thin shell: this module builds the command tree from the
feature modules, resolves credentials (re-minting the JWT from the account's
session cookie when needed) and hands control to the selected feature.
"""
import argparse
import os
import sys
from . import __version__
from .accounts import Accounts
from .client import TripoClient
from .config import Config
from .errors import TripoAPIError, TripoTimeout, is_insufficient_credits
from .features import MODULES
from .output import Out
EPILOG = """\
常用示例:
tripocli account add --cookie "<粘贴浏览器 cookie>" # 一次性添加账号,之后免浏览器
tripocli account list # 多账号查看/切换
tripocli serve --open # 打开网页版账号管理(看积分/有效期)
tripocli update # 改完源码后更新全局 CLI
tripocli balance # 查看积分
tripocli text -p "a wooden barrel" --wait -o a.glb # 文生 3D
tripocli image -i ./photo.png --wait -o b.glb # 单图生 3D
tripocli multiview --front f.png --left l.png --right r.png --back b.png --wait -o c.glb
tripocli batch -i a.png,b.png --wait -o ./out # 批量图生 3D
tripocli remesh -p <project_id> --face-limit 8000 --wait -o low.glb
tripocli rig -p <project_id> --type biped --wait # 绑定骨骼
tripocli animate -p <project_id> --preset idle --wait -o anim.glb
tripocli segment -p <project_id> --wait # 智能分割
tripocli texture -p <project_id> --resolution 2K --wait
tripocli pbr -p <project_id> --wait # PBR 材质
tripocli upscale -p <project_id> --wait # 纹理放大
tripocli export -p <project_id> -f fbx --wait -o m.fbx
凭据:只需提供一次浏览器 cookie,CLI 会自行续期 JWT(cookie 约 30 天有效)。
也可用环境变量 TRIPO_COOKIE 提供;多账号用 --account <名字> 指定。
加 --json 可输出机器可读结果(Agent 调用推荐)。
"""
# Commands that must run without any credentials.
NO_CREDENTIAL_COMMANDS = {"account", "config", "serve", "update", "inspect", "optimize"}
class Context:
"""Everything a feature needs: client, config, accounts, output sink and args."""
def __init__(self, client, config, accounts, out, args):
self.client = client
self.config = config
self.accounts = accounts
self.out = out
self.args = args
def _add_json_flag_everywhere(parser):
"""Allow ``--json`` after the subcommand as well as before it.
``argparse`` only accepts a parent parser's options before the subcommand, but
agents habitually append ``--json`` at the end; SUPPRESS keeps the subcommand
copy from overwriting a value given on the main parser.
"""
for action in parser._actions:
if isinstance(action, argparse._SubParsersAction):
for subparser in action.choices.values():
if "--json" not in subparser._option_string_actions:
subparser.add_argument(
"--json", action="store_true", default=argparse.SUPPRESS,
help="输出结构化 JSON(Agent 调用推荐)",
)
_add_json_flag_everywhere(subparser)
def build_parser():
parser = argparse.ArgumentParser(
prog="tripocli",
description="Tripo AI 3D 生成 CLI(面向 Agent 与开发者)",
epilog=EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("--json", action="store_true", help="输出结构化 JSON(Agent 调用推荐)")
parser.add_argument("--account", help="使用指定账号(默认用当前账号)")
parser.add_argument(
"--no-fallback", action="store_true",
help="关闭「额度不足自动切换下一个账号」(默认开启;设 TRIPO_AUTO_FALLBACK=0 也可关闭)",
)
parser.add_argument("--token", help="临时覆盖 Bearer JWT")
parser.add_argument("--device-id", help="临时覆盖 x-tripo-device-id")
parser.add_argument("--api-base", help="临时覆盖 API 根地址")
parser.add_argument("-V", "--version", action="version", version=f"tripocli {__version__}")
subparsers = parser.add_subparsers(dest="command", metavar="<命令>")
for module in MODULES:
if hasattr(module, "register"):
module.register(subparsers)
else:
command = subparsers.add_parser(module.NAME, help=module.HELP)
module.add_arguments(command)
command.set_defaults(func=module.run)
_add_json_flag_everywhere(parser)
return parser
def main(argv=None):
parser = build_parser()
args = parser.parse_args(argv)
handler = getattr(args, "func", None)
if handler is None:
parser.print_help()
return 0
out = Out(as_json=args.json)
config = Config()
accounts = Accounts()
needs_credentials = args.command not in NO_CREDENTIAL_COMMANDS
# 默认开启账号回退;显式 --no-fallback 或 TRIPO_AUTO_FALLBACK=0 可关闭。
env_fallback = os.environ.get("TRIPO_AUTO_FALLBACK", "").strip().lower()
fallback = not args.no_fallback and env_fallback not in ("0", "false", "no")
if needs_credentials and fallback and len(accounts.names()) > 1:
order = accounts.rotation(args.account)
else:
order = [args.account]
extra_codes = config.data.get("insufficient_credit_codes") or []
last_error = None
for index, account in enumerate(order):
client = TripoClient(
token=args.token, device_id=args.device_id, api_base=args.api_base,
account=account, accounts=accounts, config=config,
)
ctx = Context(client=client, config=config, accounts=accounts, out=out, args=args)
try:
if needs_credentials:
client.ensure_credentials()
handler(ctx)
return 0
except KeyboardInterrupt:
out.error("已中断")
return 130
except (TripoAPIError, TripoTimeout, ValueError, FileNotFoundError, RuntimeError) as error:
can_retry = (
needs_credentials
and fallback
and index + 1 < len(order)
and is_insufficient_credits(error, extra_codes)
)
if can_retry:
out.notice(f"⚠️ 账号 {account or accounts.active_name()} 额度不足,切换到下一个账号重试…")
last_error = error
continue
out.error(error)
return 1
except Exception as error: # unexpected: keep stdout parseable for agents
out.error(f"运行错误: {error}")
return 1
out.error(last_error or "没有可用账号")
return 1
if __name__ == "__main__":
sys.exit(main())