tripocli v2.0.0:Tripo AI 3D 生成与加工 CLI(面向 Agent 与开发者,纯 Python 标准库、零第三方依赖)
覆盖文生 3D、图生 3D、多视图、批量生成,以及重拓扑、绑定、动画、智能分割、 纹理、PBR、智能 UV、部件补全、格式导出与下载。 代码组织:一个功能一个模块(tripo/features/),入口薄壳 tripocli.py。
This commit is contained in:
commit
977f97995a
60 files changed
+5622
No files matched your search
+168
@@ -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())
|
||||
Reference in new issue
Block a user