tripo-cli 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,7 +35,7 @@ Works with both Tripo regions from the same install — international (`openapi.
35
35
  | `tripo login / logout / whoami / use` | device flow: browser approves, key arrives automatically (paste fallback); stored 0600 in `~/.tripo/config.json`; `use` switches accounts |
36
36
  | `tripo topup / balance / usage` | open the billing page + detect the credits arriving; check spend |
37
37
  | `tripo generate <endpoint>` | deterministic access to all 8 generation endpoints |
38
- | `tripo model / anim / mesh <step>` | refine, texture, stylize, convert, import, rig-check, rig, retarget, segment, complete, decimate, smartsegment |
38
+ | `tripo model / anim / mesh <step>` | refine, texture, stylize, convert, import, rig-check, rig, retarget, segment, complete, decimate, uv, smartsegment |
39
39
  | `tripo task get/list/watch` | query and stream task progress (NDJSON in `--json` mode) |
40
40
  | `tripo files upload` | upload a file, print the `file_token` |
41
41
  | `tripo batch run <manifest.yaml>` | bulk pipelines with concurrency, retries, and resume |
@@ -11,7 +11,7 @@ const SUBS = {
11
11
  generate: ['text-to-model', 'image-to-model', 'multiview-to-model', 'text-to-image', 'image-to-image', 'image-to-multiview', 'edit-multiview', 'image-to-splat'],
12
12
  model: ['refine', 'texture', 'stylize', 'convert', 'import'],
13
13
  anim: ['check', 'rig', 'retarget'],
14
- mesh: ['segment', 'complete', 'decimate', ...(isMeshEditEnabled() ? ['edit'] : []), 'smartsegment'],
14
+ mesh: ['segment', 'complete', 'decimate', 'uv', ...(isMeshEditEnabled() ? ['edit'] : []), 'smartsegment'],
15
15
  task: ['get', 'list', 'watch'],
16
16
  files: ['upload'],
17
17
  batch: ['run'],
@@ -77,7 +77,9 @@ function collectStepArgs(step, flags) {
77
77
  case 'retarget':
78
78
  return pick(['animation', 'out_format', 'animate_in_place']);
79
79
  case 'decimate':
80
- return pick(['face_limit', 'quad', 'bake']);
80
+ return pick(['model', 'face_limit', 'quad', 'smart_uv', 'bake']);
81
+ case 'uv':
82
+ return pick(['bake', 'export_orientation']);
81
83
  case 'segment':
82
84
  return pick(['model']);
83
85
  case 'complete':
@@ -147,7 +149,7 @@ export function registerProcessCommands(program) {
147
149
  await makeStepAction('retarget')(input, flags);
148
150
  });
149
151
  // ---- tripo mesh ----
150
- const mesh = program.command('mesh').description('mesh ops: segment / complete / decimate / smartsegment');
152
+ const mesh = program.command('mesh').description('mesh ops: segment / complete / decimate / uv / smartsegment');
151
153
  withTaskOptions(mesh.command('segment [input]').description('segment a model into parts').option('--model <v>', 'v1.0-20250506|v2.0-20260430')).action(makeStepAction('segment'));
152
154
  withTaskOptions(mesh
153
155
  .command('complete [input]')
@@ -156,9 +158,16 @@ export function registerProcessCommands(program) {
156
158
  withTaskOptions(mesh
157
159
  .command('decimate [input]')
158
160
  .description('high-poly → low-poly (bakes textures by default)')
161
+ .option('--model <v>', 'v3.0|v2.0|v1.0 (default v2.0)')
159
162
  .option('--face-limit <n>', 'target faces 500–20000 tri / 500–10000 quad', (v) => Number(v))
160
163
  .option('--quad', 'quad output')
164
+ .option('--smart-uv', 'unwrap UVs in the same task (v3.0 only; selects v3.0 when --model is omitted)')
161
165
  .option('--no-bake', 'skip baking textures onto the low-poly')).action(makeStepAction('decimate'));
166
+ withTaskOptions(mesh
167
+ .command('uv [input]')
168
+ .description('smart UV unwrap; remaps textures/rig/animation (input ≤80k equivalent triangles)')
169
+ .option('--no-bake', 'skip baking textures onto the new UV layout (geometry + UVs only)')
170
+ .option('--export-orientation <o>', '+x|-x|+y|-y')).action(makeStepAction('uv'));
162
171
  // Hidden until /v3/mesh/edit is live server-side (see isMeshEditEnabled).
163
172
  if (isMeshEditEnabled()) {
164
173
  withTaskOptions(mesh
@@ -73,7 +73,8 @@ export async function downloadArtifacts(task, outputDir, historyEntry, options =
73
73
  const ext = extname(name).toLowerCase();
74
74
  if (!modelFile && MODEL_EXTS.has(ext))
75
75
  modelFile = dest;
76
- if (!preview && (IMAGE_EXTS.has(ext) || /rendered|preview/i.test(field)))
76
+ // uv_render (mesh/uv) is a flat UV atlas, not a render of the model.
77
+ if (!preview && !/uv_render/.test(field) && (IMAGE_EXTS.has(ext) || /rendered|preview/i.test(field)))
77
78
  preview = dest;
78
79
  }
79
80
  // Normalize the preview name so agents can rely on preview.png existing.
@@ -17,6 +17,7 @@ export const MODEL_PRODUCING_TYPES = new Set([
17
17
  'mesh_segmentation',
18
18
  'mesh_completion',
19
19
  'mesh_edit',
20
+ 'mesh_uv',
20
21
  'animate_rig',
21
22
  'animate_retarget',
22
23
  ]);
@@ -32,6 +33,7 @@ export const STEPS = [
32
33
  { name: 'segment', endpoint: '/v3/mesh/segment', produces: 'mesh_segmentation', accepts: 'any-model', acceptsFile: true, describe: { zh: '把网格分割成部件', en: 'segment the mesh into parts' } },
33
34
  { name: 'complete', endpoint: '/v3/mesh/complete', produces: 'mesh_completion', accepts: ['mesh_segmentation'], acceptsFile: false, describe: { zh: '补全分割后的部件', en: 'complete the segmented parts' } },
34
35
  { name: 'decimate', endpoint: '/v3/mesh/decimate', produces: 'highpoly_to_lowpoly', accepts: 'any-model', acceptsFile: true, aliases: ['lowpoly'], describe: { zh: '减面到目标面数', en: 'decimate to a target face count' } },
36
+ { name: 'uv', endpoint: '/v3/mesh/uv', produces: 'mesh_uv', accepts: 'any-model', acceptsFile: true, describe: { zh: '智能展 UV,原贴图/骨骼/动画映射到新布局', en: 'smart UV unwrap; remaps textures/rig/animation onto the new layout' } },
35
37
  // mesh/edit: local retry/refine inside a bbox, P2 pipeline (model fixed to P2-20260801).
36
38
  { name: 'edit', endpoint: '/v3/mesh/edit', produces: 'mesh_edit', accepts: 'any-model', acceptsFile: true, describe: { zh: '在 bbox 区域内局部重做(P2 管线)', en: 'redo one bbox region locally (P2 pipeline)' } },
37
39
  // smartsegment's `input` is file_token/URL only (the server does not resolve
@@ -109,7 +109,7 @@ export const TEXTURE_MODELS = ['v3.0-20250812', 'v2.5-20250123'];
109
109
  /** mesh_segmentation whitelist (ALLOWED_SEGMENT_MODELS). */
110
110
  export const SEGMENT_MODELS = ['v1.0-20250506', 'v2.0-20260430'];
111
111
  /** mesh/decimate whitelist (ALLOWED_DECIMATE_MODELS; blank/"default" → server default v2.0). */
112
- export const DECIMATE_MODELS = ['v2.0', 'v1.0'];
112
+ export const DECIMATE_MODELS = ['v3.0', 'v2.0', 'v1.0'];
113
113
  /** Server whitelists differ per endpoint (ImageGenerationValidation). */
114
114
  export const TEXT_TO_IMAGE_TEMPLATES = [
115
115
  'asset_extraction',
@@ -5,7 +5,7 @@
5
5
  * Every rule here prevents a guaranteed 400 or a silently wasted credit.
6
6
  * Rules marked [hard] mirror server enforcement; [soft] are CLI smart defaults.
7
7
  */
8
- import { CONVERT_FORMATS, EXPORT_ORIENTATIONS, FBX_PRESETS, MODEL_P2, P1_FACE_LIMIT, P1_FORBIDDEN_PARAMS, P2_FACE_LIMIT, P2_FORBIDDEN_PARAMS, TEXTURE_FORMATS, TEXTURE_QUALITIES, isP1, isP2, normalizeModel, } from './models.js';
8
+ import { CONVERT_FORMATS, DECIMATE_MODELS, EXPORT_ORIENTATIONS, FBX_PRESETS, MODEL_P2, P1_FACE_LIMIT, P1_FORBIDDEN_PARAMS, P2_FACE_LIMIT, P2_FORBIDDEN_PARAMS, TEXTURE_FORMATS, TEXTURE_QUALITIES, isP1, isP2, normalizeModel, } from './models.js';
9
9
  const PROMPT_MAX = 1024;
10
10
  const NEGATIVE_PROMPT_MAX = 255;
11
11
  /** Validate + auto-fix parameters for 3D generation (text/image/multiview-to-model). */
@@ -168,7 +168,8 @@ export function validateConvertParams(input) {
168
168
  }
169
169
  /**
170
170
  * Validate parameters for mesh/decimate (ModelPostProcessValidation):
171
- * v2.0 (default): face_limit 500–20000 tri / 500–10000 quad.
171
+ * v2.0 (default) / v3.0: face_limit 500–20000 tri / 500–10000 quad.
172
+ * v3.0 only: smart_uv (UV unwrap in the same task).
172
173
  * v1.0: face_limit REQUIRED, 500–2,000,000 tri / 500–150,000 quad; no bake/part_names.
173
174
  */
174
175
  export function validateDecimateParams(input) {
@@ -176,8 +177,18 @@ export function validateDecimateParams(input) {
176
177
  const warnings = [];
177
178
  const errors = [];
178
179
  const model = typeof params.model === 'string' ? params.model.toLowerCase() : undefined;
179
- if (model !== undefined && !['v1.0', 'v2.0', 'default'].includes(model)) {
180
- errors.push(`decimate model must be v2.0 or v1.0 (got "${String(params.model)}")`);
180
+ if (model !== undefined && model !== 'default' && !DECIMATE_MODELS.includes(model)) {
181
+ errors.push(`decimate model must be ${DECIMATE_MODELS.join('/')} (got "${String(params.model)}")`);
182
+ }
183
+ // [hard] smart_uv is v3.0-only (server: 1004 on v1.0/v2.0); [soft] no model given → v3.0.
184
+ if (params.smart_uv === true) {
185
+ if (model === undefined) {
186
+ params.model = 'v3.0';
187
+ warnings.push('smart_uv requires decimate v3.0: using model=v3.0 (35 credits tri / 40 quad; v2.0 is 30)');
188
+ }
189
+ else if (model !== 'v3.0') {
190
+ errors.push(`smart_uv only works with decimate model v3.0 (got "${String(params.model)}")`);
191
+ }
181
192
  }
182
193
  const isV1 = model === 'v1.0';
183
194
  const quad = params.quad === true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tripo-cli",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Tripo 3D intelligent CLI — turn a prompt or an image into engine-ready 3D assets from your terminal (Tripo V3 API)",
5
5
  "keywords": [
6
6
  "tripo",
package/skill/SKILL.md CHANGED
@@ -50,7 +50,7 @@ tripo make @last --then convert:fbx --json --yes # continue from last t
50
50
  ```
51
51
 
52
52
  - `--for` scenario presets: `game-mobile` `game-pc` `film` `print` `ar-web` `anim` `toy`
53
- - `--then` chain steps: `refine texture stylize convert import rig-check rig retarget segment complete decimate smartsegment`
53
+ - `--then` chain steps: `refine texture stylize convert import rig-check rig retarget segment complete decimate uv smartsegment`
54
54
  - step args: `convert:format=FBX,texture_size=2048` (bare value = primary arg: `convert:fbx`, `stylize:lego`, `decimate:5000`)
55
55
  - Output JSON: `{"task_id","type","status","credits_consumed","output_dir","files",["model_file"],["preview"],["chain"],["credits_breakdown"]}` — with `--then`, `credits_consumed` is the **total** for the run and `credits_breakdown` itemizes every task (`[{task_id,type,credits_consumed}]`, generation first)
56
56
 
@@ -40,7 +40,7 @@ In a TTY it then asks `Run this plan?`; answering no exits 0 with `cancelled; no
40
40
  ## Options
41
41
 
42
42
  - `--for <scenario>` — `game-mobile` `game-pc` `film` `print` `ar-web` `anim` `toy`. Sets model+params+chain+format from the domain knowledge base.
43
- - `--then <steps>` — processing chain, comma-separated. Steps: `refine texture stylize convert import rig-check rig retarget segment complete decimate smartsegment`. Args: `step:key=value,...`; bare value maps to the step's primary arg (`convert:fbx` → format, `stylize:lego` → style, `decimate:5000` → face_limit, `retarget:preset:walk` → animation). Overrides the scenario chain.
43
+ - `--then <steps>` — processing chain, comma-separated. Steps: `refine texture stylize convert import rig-check rig retarget segment complete decimate uv smartsegment`. Args: `step:key=value,...`; bare value maps to the step's primary arg (`convert:fbx` → format, `stylize:lego` → style, `decimate:5000` → face_limit, `retarget:preset:walk` → animation). Overrides the scenario chain.
44
44
  - `--model <m>` — force `tripo-v3.1` (high fidelity, default), `tripo-p1` (low-poly; face_limit 50–20000; quad/parts/geometry_quality unsupported and stripped) or `tripo-p2` (P-series preview; adds quad, face_limit 48–50000 tri / 48–25000 quad; never auto-selected). The CLI normalizes these aliases to the wire versions (`v3.1-20260211` / `P1-20260311` / `P2-20260801`) the server actually accepts. Full table with status (GA / Preview / Legacy) and official links: `tripo docs --topic commands/generate` → "Models & Versions".
45
45
  - `-n, --candidates <n>` — up to 4 parallel candidates with different seeds (interactive pick when chaining).
46
46
  - `--seed <n>` — fixed model_seed for reproducible geometry.
@@ -92,7 +92,8 @@ All prefixed `preset:biped:`
92
92
  ```bash
93
93
  tripo mesh segment [input] [--model v1.0-20250506|v2.0-20260430]
94
94
  tripo mesh complete [input] [--completion-mode ai_completion|quick_cap] # needs a segment task
95
- tripo mesh decimate [input] --face-limit 5000 [--quad] [--no-bake] # 500–20000 tri / 500–10000 quad
95
+ tripo mesh decimate [input] --face-limit 5000 [--model v3.0|v2.0|v1.0] [--quad] [--smart-uv] [--no-bake] # 500–20000 tri / 500–10000 quad
96
+ tripo mesh uv [input] [--no-bake] [--export-orientation +x|-x|+y|-y] # input ≤ 80,000 equivalent triangles
96
97
  tripo mesh smartsegment <file-or-url> [--seg-type image|model] [--granularity coarse|medium|fine] [--hint "..."]
97
98
  ```
98
99
 
@@ -100,9 +101,12 @@ tripo mesh smartsegment <file-or-url> [--seg-type image|model] [--granularity co
100
101
  | --- | --- | --- |
101
102
  | `segment` | task / file / URL | `model` `v1.0-20250506` (default, geometry-based) / `v2.0-20260430` (Beta, semantic labels + geometry). **v2 only**: `segmentation_granularity` simple/balanced/detailed (default balanced), `split_by_connectivity` (default true), `ref_image` file_token/URL — when `ref_image` is set the other two are ignored; setting `ref_image` on v1 → `1004` |
102
103
  | `complete` | **segment task only** | `completion_mode` `ai_completion` (AI diffusion, default) / `quick_cap` (fast hole-fill, no AI); `part_names` (default: all parts); `model` `v1.0-20250506` (only version) |
103
- | `decimate` | task / file | `face_limit` (docs quote 1000–20000; CLI accepts 500–20000 tri / 500–10000 quad); `quad`; `bake` (default true — bake textures onto the low-poly, right for LODs); `part_names`. `-p model=v1.0`: up to 2,000,000 tri / 150,000 quad but `face_limit` becomes required and `bake`/`part_names` are unsupported |
104
+ | `decimate` | task / file / URL | `model` `v2.0` (default, smart retopology, 30 credits) / `v3.0` (newest retopology, 35 credits tri / 40 quad) / `v1.0` (basic decimation, 10 credits); `face_limit` (v2.0/v3.0: optional, 500–20000 tri / 500–10000 quad, adaptive if omitted); `quad`; `bake` (default true — bake textures onto the low-poly, right for LODs); `part_names`; `smart_uv` (**v3.0 only**, default false — unwrap UVs in the same task, no extra cost, no UV atlas image; `--smart-uv` without `--model` selects v3.0, an explicit v1.0/v2.0 is rejected locally because the server answers `1004`). `--model v1.0`: up to 2,000,000 tri / 150,000 quad but `face_limit` becomes required and `bake`/`part_names` are unsupported |
105
+ | `uv` | task / file / URL | re-unwraps UVs and remaps the existing textures, rig and animation onto the new layout; **20 credits**. `bake` (default true; `--no-bake` returns geometry + UVs only); `export_orientation` `+x`/`-x`/`+y`/`-y`; `compress` `geometry` (meshopt, `-p compress=geometry`); `model` `default` (only value). **Input ≤ 80,000 equivalent triangles** (a quad counts as two): larger inputs are accepted, then the task fails and refunds — decimate first |
104
106
  | `smartsegment` | **file / URL only (no task ids)** | `seg_type` `image` (photo → auto-model → segment, **85 credits**) / `model` (existing **GLB only** → segment, **55 credits**; CLI default); `granularity` coarse/medium/fine (default medium); `hint` free text describing the parts; `transform` 4×4 column-major matrix required for `model` — the CLI auto-fills identity |
105
107
 
106
108
  - `segment` vs `smartsegment`: segment only splits an existing model; smartsegment is the end-to-end pipeline (asset → auto modelling → segmentation).
107
109
  - smartsegment output: `prompt` (comma-separated part labels), `mask_url` (PNG semantic mask), `seg_model_url` (segmented GLB), `seg_task_id` (inner segmentation task), `model_task_id` (inner image_to_model / import task). Task types: `smartsegment_image` / `smartsegment_model`.
108
110
  - `mesh complete` only accepts segmentation tasks; segmented / completed parts can then be exported selectively via `convert -p 'part_names=[...]'`.
111
+ - UVs in one task or two: `decimate --smart-uv` (v3.0) unwraps during retopology — one task, no atlas image. `uv` is its own task that also returns the UV atlas; run it on models that are already under the limit, e.g. `--then decimate:20000,uv`.
112
+ - uv output: the model plus `uv_render.webp` (the UV atlas). It is a flat layout, not a render of the model, so the CLI does not copy it to `preview.png`.
@@ -4,7 +4,7 @@
4
4
  | --- | --- | --- | --- |
5
5
  | 3 | 1002 | authentication failed (invalid/missing API key) | set `TRIPO_API_KEY` or run `tripo login`; check the key wasn't revoked |
6
6
  | 3 | 1005 | forbidden (no permission for this resource) | check account permissions |
7
- | 2 | 1003/1004 | malformed request / invalid parameter | check spelling against `tripo docs --topic commands/<cmd>`; the CLI already strips illegal P1 params. Classic 1004s: `generate_parts=true` with texture/pbr on, `ref_image` on segment v1, `quad=true` on P1, non-GLB input to smartsegment `model` / rig-check. `invalid model`: the API only accepts dated wire values (`P2-20260801`, `v3.1-20260211`…) — pass the CLI alias (`--model tripo-p2`) and let the CLI normalize, or the wire value itself; never `tripo-*` straight to the API. Table: `commands/generate` § "Models & Versions" |
7
+ | 2 | 1003/1004 | malformed request / invalid parameter | check spelling against `tripo docs --topic commands/<cmd>`; the CLI already strips illegal P1 params. Classic 1004s: `generate_parts=true` with texture/pbr on, `ref_image` on segment v1, `quad=true` on P1, `smart_uv=true` on decimate v1.0/v2.0, non-GLB input to smartsegment `model` / rig-check. `invalid model`: the API only accepts dated wire values (`P2-20260801`, `v3.1-20260211`…) — pass the CLI alias (`--model tripo-p2`) and let the CLI normalize, or the wire value itself; never `tripo-*` straight to the API. Table: `commands/generate` § "Models & Versions" |
8
8
  | 9 | 1007 | per-API-key rate limit | wait and retry with backoff; honour `Retry-After` / `X-RateLimit-Reset` headers |
9
9
  | 9 | 2000 | per-account concurrency pool full (see table below) | wait for a task in that category to finish, or lower `--concurrency` in batch; other categories are unaffected |
10
10
  | 8 | 2001 | task not found | task ids are account-scoped — same key that created it? right region (ov/cn)? |
@@ -42,6 +42,7 @@ Pools are independent: a full H-series pool does not block P-series or image tas
42
42
  - `complete` only accepts a `segment` task; `retarget` only accepts a `rig` task
43
43
  - quad + GLTF export → rejected (quads cannot be stored in glTF); use FBX/OBJ/USDZ
44
44
  - P1 + face_limit outside 50–20000 → rejected with a suggestion to use v3.1
45
+ - decimate `smart_uv` on an explicit v1.0/v2.0 → rejected (v3.0 only); with no model given the CLI selects v3.0
45
46
  - multiview without a front view → rejected (name a file "front" or pass it first)
46
47
  - `-n` with `--then` needs an interactive terminal (pick the winner first, then chain)
47
48
 
@@ -57,3 +58,4 @@ All of these surface in `tripo make ... --dry-run --json` as `errors` (blocking)
57
58
  - A downstream step after a generation that used `export_orientation` succeeds but comes out **wrongly oriented** — no error is raised. Set the orientation on the final `convert` step instead.
58
59
  - `generate_parts` with `smart_low_poly=true` silently produces no parts (smart_low_poly wins); with `quad=true` the parts are triangles.
59
60
  - `image-to-splat` yields a `.splat`, not a mesh — texture / rig / convert steps cannot follow it.
61
+ - `uv` accepts any model at creation, but an input over 80,000 equivalent triangles fails at run time (credits refunded) — decimate first: `--then decimate:20000,uv`.