The tmc CLI - The Modding Community

尚未发布

tmc 是我们公开内容 API 的命令行客户端。它可以创建、编辑和删除你的素材、模组、服务器、文章、社区、合集与群组——而且,正因为这才是 API 的实际用途,它能用一条命令上传文件并发布版本。

Python 3.10+零依赖MIT 许可

tmc 尚未发布。 它不在 PyPI 上,也没有公开仓库——目前只能从源码检出安装,而本页描述的是这个工具已经能做的事,而不是在宣布上线。

install.sh
# From the source checkout — there is nothing to build
pip install .
pip install '.[fast]'   # optional: cryptography, for faster signing
tmc --version       # tmc 1.1.0

# Keys are made under Account → API Keys
tmc auth login --token tmc_xxxxxxxxxxxx
tmc auth whoami

# …or read public items with no key at all
tmc mod list --anon --app 4 -o json

只有一个纯标准库的包,因此既不用编译也没有依赖要解析——而且每一条命令、每一个参数和每一个退出码都写进了文档。

版本发布

需要一个工具的理由

用原始 API 发布一个版本,是按特定顺序发出的四个请求:上传文件、收集它们的 id、读取已有的版本集合,然后把新的写回去,并且不打扰其他版本。release publish 就是这一整套流程。

publish.sh
# Upload the files, attach them, write the release — one command
tmc release publish --mod 5 \
  --version 1.2.0 \
  --title "Bug fixes" \
  --content-file CHANGELOG.md \
  --file 'dist/*.zip' --file dist/checksums.txt

# Everything on the item, without re-sending the item
tmc release list  --mod 5
tmc release files --mod 5 --version 1.2.0

重复执行即为更新

同一个 --version 执行两次会更新该版本,并把新文件合并进它的集合。你的其他版本永远不会被动到,隐藏的版本也仍然隐藏。

通配符,自动分批

传入 --file 'dist/*.zip' 以及任意多的文件;每次请求最多二十个上传的限制由工具替你处理。

大文件采用流式传输

1 GB 的上传是分块从磁盘读取的,而不是整个读进内存。任何超出你密钥大小限制的文件都会在发送之前被标记出来。
它能做什么

API 能做的一切,一一列明

你在浏览器里会做的每件事都有一条对应命令,还有几件你在浏览器里并不想做的事——批量编辑、流水线和脚本化发布。

完全没有依赖

Python 3.10+ 加标准库。把它丢到一台没有包索引的构建机或游戏服务器上,它照样能跑。若恰好装了 cryptography 就用它签名,没有则使用内置的 RFC 8032 实现——两种情况下签名完全相同。

两种认证模式都支持

Bearer tmc_ 密钥,或者 Ed25519 签名断言——私钥留在你手里,我们只保存公钥那一半。

所有类型共用一套语法

同样的五个动词——list、get、create、update、delete——适用于全部十四种类型,其中十二种还有各自的专用命令。tmc content <type> 是统一形式,也是访问独立的 release 与 media 类型的唯一途径。

操作关联无需重发全部内容

标签、媒体、版本、链接、来源和合集条目都单独管理,因此添加一张截图并不意味着要重写整个条目。

批量操作,处处不越界

从任意长度的 JSON 文件创建或编辑。每一项服务端上限——单次写入 25 条、单次删除 100 条、关联成员 200 个、删除关联时 500 个键、上传 20 个分片——都会替你自动分批,出现部分失败时会指明是哪个元素出错,让你可以续做而不是从头再来。

拼写错误在本地就被抓住

字段名会与 API 模式的本地镜像比对,并给出最接近的提示:“mod”没有字段“tgs”。你是想写“tags”吗? tmc schema mod 会打印它用来比对的字段清单。

为管道而生

七种输出格式——table、json、jsonl、csv、tsv、yaml、ids——并可用 --field 只保留你要的列。数据走 stdout,进度走 stderr,还有 tmc completion bash|zsh|fish 适配你正在用的 shell。

限流自动处理

遇到 429 会严格按照 API 要求的时长等待,最长不超过 --retry-wait-max,然后告诉你还剩多久,而不是让构建一直挂着。退避只作用于幂等方法——POST 绝不会被悄悄重发。
凭据

密钥、配置档与公钥那一半

登录一次,凭据便会以 0600 权限保存在 ~/.config/tmc/config.json 中——或者通过环境变量传入,则完全不碰磁盘。每个站点或每把密钥一个配置档,而对于本来就不需要密钥的那一半 API,还有 --anon。

auth.sh
# A bearer key, or an Ed25519 key you hold the private half of
tmc auth login --token tmc_xxxxxxxxxxxx
tmc auth login --jwt --key-id tmcak_xxxxxxxx --private-key ~/keys/tmc.pem

# A second key on the same site — a scoped one for scripts
tmc auth login --profile ci \
  --base-url https://moddingcommunity.com --token tmc_…
tmc auth use ci

# Which key is active, and what the server lets it do
tmc auth whoami
tmc auth doctor

# …or no key at all
tmc mod list --anon --app 4 -o json

按站点或按密钥划分配置档

--profile 指定其中之一,tmc auth use 切换默认项,而且每一个存储值都有对应的环境变量——TMC_TOKEN、TMC_KEY_ID、TMC_PRIVATE_KEY_FILE、TMC_BASE_URL、TMC_PROFILE。

问清楚一把密钥究竟能做什么

auth whoami 用三次刻意无害的探测来确认密钥的真实权限——一次读取、一次注定无法通过校验的创建,以及一次针对空 id 列表的删除。不会创建也不会删除任何东西;--read-only 则只发送那次读取。

给配置做个体检

auth doctor 会报告当前使用的是哪个签名后端、你的配置文件权限是否安全,以及站点是否有响应——这正是调试流水线之前最该知道的三件事。

完全不用密钥也能读取

--anon 会在不带 Authorization 头的情况下读取七种类型的公开摘要——那确实是另一个端点,所以它会在请求发出前就拒绝写操作,会丢弃该端点没有的筛选条件而不是让它们看起来生效过,也绝不会悄悄升级为使用已保存的密钥。--set apiPublic=false 就是让一个条目退出该范围的方式。
关联

四个名副其实的动词

危险的那个是 set:在这套 API 中,对关联发 PUT 意味着“现在这就是完整集合”,所以一次只带一个成员的 PUT 会删掉该条目上的其他全部内容。CLI 把这四者区分开,并在发送破坏性操作前发出警告。

命令 方法 含义
tmc rel add POST 合并这些,其余保持不变
tmc rel set PUT 现在这就是完整集合
tmc rel rm DELETE 移除你点名的那些
tmc rel clear DELETE 全部移除
relations.sh
# Add two tags. The rest of the item is untouched
tmc tags add mod 5 pvp vanilla

# Upload a screenshot and attach it in one step
tmc media add mod 5 --file shot.png --title Screenshot

# Read one relation back
tmc rel get mod 5 releases

# …and the one that REPLACES the whole gallery
tmc rel set mod 5 media --from-file gallery.json
自动化

为在 CI 中运行而生

构建你的项目,在每个标签上发布版本并上传其文件。凭据来自环境变量,因此不会在 runner 上写入任何磁盘内容。

release.yml
- name: Publish to TMC
  env:
    TMC_TOKEN: ${{ secrets.TMC_TOKEN }}
  run: |
    tmc release publish --mod 42 \
      --version "${GITHUB_REF_NAME#v}" \
      --content-file CHANGELOG.md \
      --file 'dist/*.zip'

# Clean pipes: data on stdout, progress on stderr
tmc mod list --mine --all -o ids | xargs -n1 tmc mod get -o json

不往 runner 写任何东西

凭据来自环境变量,所以不会创建配置档,也没有东西能活过这次任务。JWT 密钥可以继续作为 runner 挂载的机密文件——我们始终只持有它的公钥那一半。

有实际含义的退出码

2 用法错误、3 认证、4 未找到、5 校验、6 触发限流、7 服务端、8 网络——好让流水线分得清“重试一下”和“去修你的密钥”。

不会被过时的构建卡住

工具还不认识的字段,加上 --allow-unknown-fields 依旧能提交,而 tmc raw 可以发送任意请求。

对着真东西测试

75 项测试通过真实套接字,用进程内的 API 模拟服务驱动真正的 CLI——覆盖两种认证模式、批处理、关联语义和重试路径。

密钥在账号 → API 密钥里

密钥是免费的,几秒就能创建,并带有读 / 写 / 删除范围、有效期、IP 白名单和自己的限流额度——所以交给构建服务器的那把密钥可以只做一件事。每一条命令、每一个参数和每一个退出码都写在文档里。

密钥今天就有用——API 已经上线,文档也已写好。tmc 本身仍未发布:还没有 PyPI 包,也没有公开仓库,所以在那之前它只能从源码检出使用。