tripo-cli 0.7.1 → 0.7.3
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 +8 -8
- package/dist/ai/experts.js +4 -4
- package/dist/ai/plan-card.js +2 -1
- package/dist/ai/wizard.js +8 -4
- package/dist/commands/completion.js +1 -1
- package/dist/commands/generate.js +16 -5
- package/dist/commands/login.js +79 -21
- package/dist/commands/make.js +3 -0
- package/dist/commands/mcp.js +7 -3
- package/dist/commands/process-groups.js +26 -14
- package/dist/commands/shared.js +17 -3
- package/dist/commands/task.js +2 -0
- package/dist/commands/update.js +18 -2
- package/dist/core/analytics.js +40 -17
- package/dist/core/client.js +49 -8
- package/dist/core/device-auth.js +49 -1
- package/dist/core/download.js +2 -1
- package/dist/core/errors.js +8 -3
- package/dist/core/requests.js +36 -17
- package/dist/core/task-service.js +2 -0
- package/dist/core/upload.js +23 -5
- package/dist/core/watch.js +3 -0
- package/dist/knowledge/chains.js +4 -5
- package/dist/knowledge/error-catalog.js +10 -8
- package/dist/knowledge/models.js +28 -7
- package/dist/knowledge/params.js +48 -25
- package/dist/knowledge/scenarios.js +4 -4
- package/dist/pipeline/make.js +2 -2
- package/dist/pipeline/runner.js +25 -1
- package/dist/util/input-detect.js +10 -1
- package/package.json +1 -1
- package/skill/SKILL.md +19 -8
- package/skill/commands/account.md +7 -2
- package/skill/commands/generate.md +24 -15
- package/skill/commands/make.md +4 -3
- package/skill/commands/process.md +27 -23
- package/skill/commands/task.md +2 -2
- package/skill/common-errors.md +10 -9
- package/skill/examples/animation.md +1 -1
- package/skill/examples/game-asset.md +1 -1
package/dist/knowledge/models.js
CHANGED
|
@@ -52,6 +52,7 @@ export const P2_FORBIDDEN_PARAMS = ['smart_low_poly', 'generate_parts', 'geometr
|
|
|
52
52
|
/** Rig wire versions (backend whitelist; marketing names rig-v1.0/rig-v2.0 are rejected). */
|
|
53
53
|
export const RIG_MODEL_V1 = 'v1.0-20240301';
|
|
54
54
|
export const RIG_MODEL_V25 = 'v2.5-20260210';
|
|
55
|
+
export const RIG_MODEL_V30 = 'v3.0-20260909';
|
|
55
56
|
/** Marketing rig names → wire versions the server actually accepts. */
|
|
56
57
|
export const RIG_MODEL_ALIASES = {
|
|
57
58
|
'rig-v1.0': RIG_MODEL_V1,
|
|
@@ -59,6 +60,8 @@ export const RIG_MODEL_ALIASES = {
|
|
|
59
60
|
'rig-v2.0': RIG_MODEL_V25,
|
|
60
61
|
'rig-v2.5': RIG_MODEL_V25,
|
|
61
62
|
'v2.5-20260210': RIG_MODEL_V25,
|
|
63
|
+
'rig-v3.0': RIG_MODEL_V30,
|
|
64
|
+
'v3.0-20260909': RIG_MODEL_V30,
|
|
62
65
|
};
|
|
63
66
|
export function normalizeRigModel(input) {
|
|
64
67
|
return RIG_MODEL_ALIASES[input] ?? input;
|
|
@@ -67,8 +70,9 @@ export function normalizeRigModel(input) {
|
|
|
67
70
|
export const CAPABILITY_DEFAULTS = {
|
|
68
71
|
texture: 'v3.0-20250812',
|
|
69
72
|
/** Server default is v1.0 (biped-only presets); the CLI defaults to the
|
|
70
|
-
*
|
|
71
|
-
|
|
73
|
+
* v3.0 rig: recommended for humanoids, v2.5 behavior and presets for the
|
|
74
|
+
* other body types. */
|
|
75
|
+
rig: RIG_MODEL_V30,
|
|
72
76
|
segment: 'v1.0-20250506',
|
|
73
77
|
textToImage: 'seedream_v4',
|
|
74
78
|
imageToImage: 'seedream_v5',
|
|
@@ -81,8 +85,18 @@ export const RIG_TYPES = [
|
|
|
81
85
|
'avian',
|
|
82
86
|
'serpentine',
|
|
83
87
|
'aquatic',
|
|
88
|
+
'others',
|
|
84
89
|
];
|
|
85
|
-
|
|
90
|
+
/** Bone naming specs (biped only); actorcore/unity/unreal/vrm need rig v3.0. */
|
|
91
|
+
export const RIG_SPECS = ['tripo', 'mixamo', 'actorcore', 'unity', 'unreal', 'vrm'];
|
|
92
|
+
/** Retarget presets of non-biped rig types (rig v2.5/v3.0); avian and others have none. */
|
|
93
|
+
export const NON_BIPED_PRESETS = {
|
|
94
|
+
quadruped: ['preset:quadruped:walk'],
|
|
95
|
+
hexapod: ['preset:hexapod:walk'],
|
|
96
|
+
octopod: ['preset:octopod:walk'],
|
|
97
|
+
serpentine: ['preset:serpentine:march'],
|
|
98
|
+
aquatic: ['preset:aquatic:march'],
|
|
99
|
+
};
|
|
86
100
|
/** Per-endpoint image model whitelists (ImageGenerationValidation):
|
|
87
101
|
* image-to-image does NOT accept seedream_v4. */
|
|
88
102
|
export const TEXT_TO_IMAGE_MODELS = [
|
|
@@ -94,6 +108,8 @@ export const TEXT_TO_IMAGE_MODELS = [
|
|
|
94
108
|
'chat_image_1',
|
|
95
109
|
'chat_image_1.5',
|
|
96
110
|
'chat_image_2',
|
|
111
|
+
'chat_image_2.5_flare',
|
|
112
|
+
'chat_image_2.5_sunburst',
|
|
97
113
|
];
|
|
98
114
|
export const IMAGE_TO_IMAGE_MODELS = [
|
|
99
115
|
'seedream_v5',
|
|
@@ -103,9 +119,13 @@ export const IMAGE_TO_IMAGE_MODELS = [
|
|
|
103
119
|
'chat_image_1',
|
|
104
120
|
'chat_image_1.5',
|
|
105
121
|
'chat_image_2',
|
|
122
|
+
'chat_image_2.5_flare',
|
|
123
|
+
'chat_image_2.5_sunburst',
|
|
106
124
|
];
|
|
125
|
+
/** Texture model required by texture_quality=fast and delight. */
|
|
126
|
+
export const TEXTURE_MODEL_V35 = 'v3.5-20260815';
|
|
107
127
|
/** texture_model whitelist (ModelPostProcessValidation.ALLOWED_TEXTURE_MODELS). */
|
|
108
|
-
export const TEXTURE_MODELS = ['v3.0-20250812', 'v2.5-20250123'];
|
|
128
|
+
export const TEXTURE_MODELS = [TEXTURE_MODEL_V35, 'v3.0-20250812', 'v2.5-20250123'];
|
|
109
129
|
/** mesh_segmentation whitelist (ALLOWED_SEGMENT_MODELS). */
|
|
110
130
|
export const SEGMENT_MODELS = ['v1.0-20250506', 'v2.0-20260430'];
|
|
111
131
|
/** mesh/decimate whitelist (ALLOWED_DECIMATE_MODELS; blank/"default" → server default v2.0). */
|
|
@@ -137,9 +157,10 @@ export const TEXTURE_FORMATS = [
|
|
|
137
157
|
'TIFF',
|
|
138
158
|
'WEBP',
|
|
139
159
|
];
|
|
140
|
-
export const FBX_PRESETS = ['blender', '3dsmax', 'mixamo'];
|
|
160
|
+
export const FBX_PRESETS = ['blender', '3dsmax', 'mixamo', 'bake_scale'];
|
|
141
161
|
export const EXPORT_ORIENTATIONS = ['+x', '-x', '+y', '-y'];
|
|
142
|
-
|
|
162
|
+
/** `fast` only works with texture model v3.5 (other versions → 1004). */
|
|
163
|
+
export const TEXTURE_QUALITIES = ['fast', 'standard', 'detailed', 'extreme'];
|
|
143
164
|
/** Normalize any accepted alias to the canonical wire value; unknown values pass through. */
|
|
144
165
|
export function normalizeModel(input) {
|
|
145
166
|
return MODEL_ALIASES[input] ?? input;
|
|
@@ -164,7 +185,7 @@ export function chooseModel(intent) {
|
|
|
164
185
|
return { model: MODEL_V31, reason: 'default high-fidelity → v3.1' };
|
|
165
186
|
}
|
|
166
187
|
/** Keywords that signal low-poly intent in a prompt (used by make/ai). */
|
|
167
|
-
const LOW_POLY_HINTS = [/low[\s_-]?poly/i, /低模/, /低面/, /手游/, /voxel/i, /像素风/];
|
|
188
|
+
const LOW_POLY_HINTS = [/low[\s_-]?poly/i, /低模/, /低面/, /低多边形/, /手游/, /voxel/i, /像素风/];
|
|
168
189
|
export function detectLowPolyIntent(text) {
|
|
169
190
|
return LOW_POLY_HINTS.some((re) => re.test(text));
|
|
170
191
|
}
|
package/dist/knowledge/params.js
CHANGED
|
@@ -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, 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';
|
|
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_MODEL_V35, 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). */
|
|
@@ -44,10 +44,11 @@ export function validateGenerationParams(model, input) {
|
|
|
44
44
|
params.texture = true;
|
|
45
45
|
warnings.push('pbr=true forces texture=true (adjusted)');
|
|
46
46
|
}
|
|
47
|
-
// [hard] generate_parts outputs bare geometry: texture/pbr/quad must be off
|
|
47
|
+
// [hard] generate_parts outputs bare geometry: texture/pbr/quad must be off
|
|
48
|
+
// (texture and pbr default to true server-side, so omitting them counts as on).
|
|
48
49
|
if (params.generate_parts === true) {
|
|
49
50
|
for (const key of ['texture', 'pbr', 'quad']) {
|
|
50
|
-
if (params[key] === true) {
|
|
51
|
+
if (key === 'quad' ? params[key] === true : params[key] !== false) {
|
|
51
52
|
params[key] = false;
|
|
52
53
|
warnings.push(`generate_parts=true disables ${key} (adjusted; run "tripo model texture" afterwards)`);
|
|
53
54
|
}
|
|
@@ -88,12 +89,35 @@ export function validateGenerationParams(model, input) {
|
|
|
88
89
|
!TEXTURE_QUALITIES.includes(params.texture_quality)) {
|
|
89
90
|
errors.push(`texture_quality must be one of ${TEXTURE_QUALITIES.join('/')}`);
|
|
90
91
|
}
|
|
92
|
+
applyTextureV35(params, 'texture_version', warnings, errors);
|
|
91
93
|
// [hard] quad topology cannot be stored in GLB → output will be FBX.
|
|
92
94
|
if (params.quad === true) {
|
|
93
95
|
warnings.push('quad=true forces FBX output (quad topology cannot be stored in GLB)');
|
|
94
96
|
}
|
|
95
97
|
return { params, warnings, errors };
|
|
96
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* [hard] texture_quality=fast and delight only exist on texture model v3.5:
|
|
101
|
+
* fast on any other version is a 1004, delight is silently ignored. v3.5 is
|
|
102
|
+
* selected when no version was given. `key` is `texture_version` on the
|
|
103
|
+
* generation endpoints and `model` on /v3/models/texture.
|
|
104
|
+
*/
|
|
105
|
+
export function applyTextureV35(params, key, warnings, errors) {
|
|
106
|
+
const feature = params.texture_quality === 'fast' ? 'texture_quality=fast' : params.delight !== undefined ? 'delight' : undefined;
|
|
107
|
+
if (!feature)
|
|
108
|
+
return;
|
|
109
|
+
const version = params[key];
|
|
110
|
+
if (version === undefined) {
|
|
111
|
+
params[key] = TEXTURE_MODEL_V35;
|
|
112
|
+
warnings.push(`${feature} needs ${key}=${TEXTURE_MODEL_V35} (selected)`);
|
|
113
|
+
}
|
|
114
|
+
else if (version !== TEXTURE_MODEL_V35) {
|
|
115
|
+
if (feature === 'delight')
|
|
116
|
+
warnings.push(`delight is ignored unless ${key}=${TEXTURE_MODEL_V35}`);
|
|
117
|
+
else
|
|
118
|
+
errors.push(`texture_quality=fast requires ${key}=${TEXTURE_MODEL_V35} (got "${String(version)}")`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
97
121
|
/** Validate parameters for models/convert. */
|
|
98
122
|
export function validateConvertParams(input) {
|
|
99
123
|
const params = { ...input };
|
|
@@ -109,9 +133,12 @@ export function validateConvertParams(input) {
|
|
|
109
133
|
else {
|
|
110
134
|
params.format = format;
|
|
111
135
|
}
|
|
112
|
-
// [hard] quad topology cannot live in GLTF/GLB.
|
|
136
|
+
// [hard] quad topology cannot live in GLTF/GLB; quad output is always FBX.
|
|
113
137
|
if (params.quad === true && format === 'GLTF') {
|
|
114
|
-
errors.push('quad=true cannot export GLTF (quad topology is not storable in glTF); use FBX
|
|
138
|
+
errors.push('quad=true cannot export GLTF (quad topology is not storable in glTF); quad forces FBX output — use format=FBX');
|
|
139
|
+
}
|
|
140
|
+
else if (params.quad === true && format && format !== 'FBX') {
|
|
141
|
+
warnings.push(`quad=true forces FBX output (format ${format} is overridden)`);
|
|
115
142
|
}
|
|
116
143
|
if (params.force_symmetry === true && params.quad !== true) {
|
|
117
144
|
delete params.force_symmetry;
|
|
@@ -142,30 +169,26 @@ export function validateConvertParams(input) {
|
|
|
142
169
|
!EXPORT_ORIENTATIONS.includes(params.export_orientation)) {
|
|
143
170
|
errors.push(`export_orientation must be one of ${EXPORT_ORIENTATIONS.join('/')}`);
|
|
144
171
|
}
|
|
145
|
-
// [soft] cost transparency
|
|
146
|
-
|
|
147
|
-
const defaults = {
|
|
148
|
-
texture_size: 4096,
|
|
149
|
-
texture_format: 'JPEG',
|
|
150
|
-
bake: true,
|
|
151
|
-
pack_uv: false,
|
|
152
|
-
export_vertex_colors: false,
|
|
153
|
-
pivot_to_center_bottom: false,
|
|
154
|
-
scale_factor: 1,
|
|
155
|
-
with_animation: true,
|
|
156
|
-
animate_in_place: false,
|
|
157
|
-
export_orientation: '+x',
|
|
158
|
-
fbx_preset: 'blender',
|
|
159
|
-
quad: false,
|
|
160
|
-
flatten_bottom: false,
|
|
161
|
-
force_symmetry: false,
|
|
162
|
-
};
|
|
163
|
-
const complex = Object.entries(defaults).some(([k, dv]) => params[k] !== undefined && params[k] !== dv) || params.face_limit !== undefined || params.part_names !== undefined;
|
|
164
|
-
if (complex) {
|
|
172
|
+
// [soft] cost transparency.
|
|
173
|
+
if (isComplexConvert(params)) {
|
|
165
174
|
warnings.push('non-default convert options use the "complex convert" price tier');
|
|
166
175
|
}
|
|
167
176
|
return { params, warnings, errors };
|
|
168
177
|
}
|
|
178
|
+
/** Pricing page: a non-default value of any of these (or any face_limit) bills the complex convert tier. */
|
|
179
|
+
const COMPLEX_CONVERT_DEFAULTS = {
|
|
180
|
+
quad: false,
|
|
181
|
+
flatten_bottom: false,
|
|
182
|
+
flatten_bottom_threshold: 0.01,
|
|
183
|
+
texture_size: 4096,
|
|
184
|
+
texture_format: 'JPEG',
|
|
185
|
+
pivot_to_center_bottom: false,
|
|
186
|
+
scale_factor: 1,
|
|
187
|
+
};
|
|
188
|
+
export function isComplexConvert(params) {
|
|
189
|
+
return (params.face_limit !== undefined ||
|
|
190
|
+
Object.entries(COMPLEX_CONVERT_DEFAULTS).some(([k, dv]) => params[k] !== undefined && String(params[k]).toUpperCase() !== String(dv).toUpperCase()));
|
|
191
|
+
}
|
|
169
192
|
/**
|
|
170
193
|
* Validate parameters for mesh/decimate (ModelPostProcessValidation):
|
|
171
194
|
* v2.0 (default) / v3.0: face_limit 500–20000 tri / 500–10000 quad.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Scenario decision matrix: --for presets and the AI wizard both read this
|
|
3
3
|
* table. Changing production strategy means editing data here, not logic.
|
|
4
4
|
*/
|
|
5
|
-
import { MODEL_P1, MODEL_V31
|
|
5
|
+
import { CAPABILITY_DEFAULTS, MODEL_P1, MODEL_V31 } from './models.js';
|
|
6
6
|
export const SCENARIOS = [
|
|
7
7
|
{
|
|
8
8
|
id: 'game-mobile',
|
|
@@ -80,13 +80,13 @@ export const SCENARIOS = [
|
|
|
80
80
|
genParams: { texture: true, pbr: true },
|
|
81
81
|
chain: [
|
|
82
82
|
{ step: 'rig-check', args: {} },
|
|
83
|
-
{ step: 'rig', args: { model:
|
|
83
|
+
{ step: 'rig', args: { model: CAPABILITY_DEFAULTS.rig, spec: 'tripo', out_format: 'glb' } },
|
|
84
84
|
{ step: 'retarget', args: { animations: ['preset:idle', 'preset:walk'] } },
|
|
85
85
|
],
|
|
86
86
|
deliverables: ['GLB', 'FBX'],
|
|
87
87
|
notes: {
|
|
88
|
-
zh: ['先 rig-check 预检,不可绑骨即止损', '接 Mixamo
|
|
89
|
-
en: ['rig-check first: stop early if not riggable', 'use rig:spec=mixamo for Mixamo/Unity Humanoid', 'prompt for a T-pose, or pre-generate with text-to-image template=t_pose'],
|
|
88
|
+
zh: ['先 rig-check 预检,不可绑骨即止损', '接 Mixamo 用 rig:spec=mixamo,Unity Humanoid / Unreal 用 spec=unity / spec=unreal', '提示词建议 T-pose 站姿,或先 text-to-image template=t_pose'],
|
|
89
|
+
en: ['rig-check first: stop early if not riggable', 'use rig:spec=mixamo for Mixamo, spec=unity / spec=unreal for Unity Humanoid / Unreal', 'prompt for a T-pose, or pre-generate with text-to-image template=t_pose'],
|
|
90
90
|
},
|
|
91
91
|
},
|
|
92
92
|
{
|
package/dist/pipeline/make.js
CHANGED
|
@@ -184,9 +184,9 @@ export async function executeMake(client, plan, options) {
|
|
|
184
184
|
notify: false,
|
|
185
185
|
timeoutMs: options.timeoutMs,
|
|
186
186
|
});
|
|
187
|
-
// If the chain stopped early (not riggable) and
|
|
187
|
+
// If the chain stopped early (not riggable) and no step downloaded a model,
|
|
188
188
|
// fetch the source artifacts so the user still gets their model.
|
|
189
|
-
if (stoppedEarly && plan.generation && !options.noDownload) {
|
|
189
|
+
if (stoppedEarly && plan.generation && !options.noDownload && !results.some((r) => r.model_file)) {
|
|
190
190
|
const detail = await client.getTask(sourceTaskId);
|
|
191
191
|
const { downloadArtifacts, defaultOutputDir } = await import('../core/download.js');
|
|
192
192
|
const { slugify } = await import('../config/history.js');
|
package/dist/pipeline/runner.js
CHANGED
|
@@ -9,12 +9,14 @@ import { buildStepRequest } from '../core/requests.js';
|
|
|
9
9
|
import { runTask } from '../core/task-service.js';
|
|
10
10
|
import { t } from '../i18n.js';
|
|
11
11
|
import { getStep, validateChain } from '../knowledge/chains.js';
|
|
12
|
+
import { NON_BIPED_PRESETS } from '../knowledge/models.js';
|
|
12
13
|
export async function runChain(options) {
|
|
13
14
|
const { client, chain } = options;
|
|
14
15
|
validateChain(chain, options.sourceType);
|
|
15
16
|
const results = [];
|
|
16
17
|
let currentTaskId = options.sourceTaskId;
|
|
17
18
|
let rigTypeRecommendation;
|
|
19
|
+
let rigType;
|
|
18
20
|
for (let i = 0; i < chain.length; i++) {
|
|
19
21
|
const spec = chain[i];
|
|
20
22
|
const args = { ...spec.args };
|
|
@@ -22,6 +24,20 @@ export async function runChain(options) {
|
|
|
22
24
|
args.rig_type = rigTypeRecommendation;
|
|
23
25
|
log(t({ zh: `采用 rig-check 推荐体型:${rigTypeRecommendation}`, en: `using rig-check recommendation: ${rigTypeRecommendation}` }));
|
|
24
26
|
}
|
|
27
|
+
if (spec.step === 'rig')
|
|
28
|
+
rigType = String(args.rig_type ?? 'biped');
|
|
29
|
+
// Biped presets (preset:<name>) do not fit other bodies: use that rig type's own preset.
|
|
30
|
+
const bodyPresets = rigType ? NON_BIPED_PRESETS[rigType] : undefined;
|
|
31
|
+
if (spec.step === 'retarget' && bodyPresets) {
|
|
32
|
+
const requested = [args.animations ?? args.animation ?? []].flat().map(String);
|
|
33
|
+
if (requested.length > 0 && requested.every((a) => /^preset:[^:]+$/.test(a))) {
|
|
34
|
+
delete args.animation;
|
|
35
|
+
args.animations = bodyPresets;
|
|
36
|
+
log(t({ zh: `体型为 ${rigType},双足预设换成 ${bodyPresets.join(', ')}`, en: `rig type is ${rigType}: biped presets swapped for ${bodyPresets.join(', ')}` }));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// avian/others have no preset animations: the rigged model is the final output.
|
|
40
|
+
const noPresets = spec.step === 'rig' && chain[i + 1]?.step === 'retarget' && rigType !== 'biped' && !bodyPresets;
|
|
25
41
|
const built = buildStepRequest(spec.step, currentTaskId, args);
|
|
26
42
|
for (const w of built.warnings)
|
|
27
43
|
warn(w);
|
|
@@ -34,10 +50,18 @@ export async function runChain(options) {
|
|
|
34
50
|
notify: false,
|
|
35
51
|
timeoutMs: options.timeoutMs,
|
|
36
52
|
// Intermediate steps skip download; final step downloads.
|
|
37
|
-
noDownload: i < chain.length - 1 && spec.step !== 'rig-check',
|
|
53
|
+
noDownload: i < chain.length - 1 && spec.step !== 'rig-check' && !noPresets,
|
|
38
54
|
precheckBalance: false, // checked once by the caller
|
|
39
55
|
});
|
|
40
56
|
results.push(result);
|
|
57
|
+
if (noPresets) {
|
|
58
|
+
const message = t({
|
|
59
|
+
zh: `体型 ${rigType} 没有预设动画,已跳过 retarget,产物为绑骨模型`,
|
|
60
|
+
en: `rig type ${rigType} has no preset animations; skipped retarget and kept the rigged model`,
|
|
61
|
+
});
|
|
62
|
+
warn(message);
|
|
63
|
+
return { results, stoppedEarly: message };
|
|
64
|
+
}
|
|
41
65
|
if (spec.step === 'rig-check') {
|
|
42
66
|
const verdict = await client.getTask(result.task_id);
|
|
43
67
|
const output = verdict.output ?? {};
|
|
@@ -59,7 +59,7 @@ export function classifyInputs(args) {
|
|
|
59
59
|
case 'url':
|
|
60
60
|
if (detected.length > 1)
|
|
61
61
|
return { kind: 'multiview', values: detected };
|
|
62
|
-
return { kind: 'url-image', values: detected };
|
|
62
|
+
return { kind: MODEL_EXTS.has(urlExtension(detected[0].value)) ? 'model' : 'url-image', values: detected };
|
|
63
63
|
case 'task-id':
|
|
64
64
|
case 'history-ref':
|
|
65
65
|
case 'file-token':
|
|
@@ -76,6 +76,15 @@ export function classifyInputs(args) {
|
|
|
76
76
|
}
|
|
77
77
|
export class InputDetectError extends Error {
|
|
78
78
|
}
|
|
79
|
+
/** Lower-cased extension of a URL's path ('' when it has none). */
|
|
80
|
+
export function urlExtension(url) {
|
|
81
|
+
try {
|
|
82
|
+
return extname(new URL(url).pathname).toLowerCase();
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
79
88
|
function isExistingFile(p) {
|
|
80
89
|
try {
|
|
81
90
|
return existsSync(p) && statSync(p).isFile();
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
You (an AI coding agent) can generate production-ready 3D assets with the `tripo` CLI.
|
|
4
4
|
One command turns a text prompt or an image into local files: a 3D model (glb/fbx/stl/...), a `preview.png` render, and a `task.json` record.
|
|
5
5
|
|
|
6
|
+
When the calling host is known, set `TRIPO_CLIENT_APP` to its actual name
|
|
7
|
+
(`codex`, `workbuddy`, `zcode`, `kimi`, `deepseek`, `claude`, `cursor`, or `chatgpt`) on each
|
|
8
|
+
CLI command, including every command in a pipeline. Do not guess from the model,
|
|
9
|
+
API key, account region, or an earlier call. Set `TRIPO_DISTRIBUTION_CHANNEL` only
|
|
10
|
+
when a packaged plugin provides it; a standalone CLI install is not a plugin.
|
|
11
|
+
These values are per-call metadata, never account-profile settings.
|
|
12
|
+
|
|
6
13
|
## Install & auth
|
|
7
14
|
|
|
8
15
|
```bash
|
|
@@ -22,17 +29,20 @@ login only works on the site it was started for.
|
|
|
22
29
|
- no account yet: mainland China → `cn`, everywhere else → `ov`
|
|
23
30
|
|
|
24
31
|
```bash
|
|
25
|
-
tripo login --region <cn|ov> # the site the human named
|
|
32
|
+
tripo login --region <cn|ov> --no-wait # the site the human named
|
|
26
33
|
```
|
|
27
34
|
|
|
28
|
-
It prints a verification URL and a one-time code
|
|
29
|
-
|
|
30
|
-
and code to the human and tell them: sign in on that page first if it asks
|
|
35
|
+
It prints a verification URL and a one-time code and exits at once. Relay the
|
|
36
|
+
URL and code to the human and tell them: sign in on that page first if it asks
|
|
31
37
|
(sign-up gives free credits), enter the code, pick an API key if the account has
|
|
32
|
-
several (one is created automatically if there are none), click Authorize.
|
|
38
|
+
several (one is created automatically if there are none), click Authorize. When
|
|
39
|
+
they say it is done, run the same command again: it finishes the login with that
|
|
40
|
+
same code (`"status":"logged_in"`), or answers `"status":"pending"` if they have
|
|
41
|
+
not authorized yet. Without `--no-wait` the command itself waits for the approval
|
|
42
|
+
(up to ~15 minutes); if it gets cut off, rerunning resumes the same code. The
|
|
33
43
|
key is then saved locally; never ask the human to paste a key into the chat. If
|
|
34
44
|
the code expires (exit 3), just run the same command again; if the human says
|
|
35
|
-
the page is the wrong site,
|
|
45
|
+
the page is the wrong site, rerun with the other `--region`.
|
|
36
46
|
|
|
37
47
|
Regions (ov = international, cn = China mainland) share one CLI: `tripo login --key tsk_...` auto-detects which region the key belongs to. Already logged into the wrong site? `tripo login --region <other>` replaces the active profile's key and region. On exit code 3 (auth), run `tripo doctor` — it diagnoses key-vs-region mismatches and prints the exact fix (`tripo config set region cn|ov`).
|
|
38
48
|
|
|
@@ -58,9 +68,10 @@ tripo make @last --then convert:fbx --json --yes # continue from last t
|
|
|
58
68
|
```
|
|
59
69
|
|
|
60
70
|
- `--for` scenario presets: `game-mobile` `game-pc` `film` `print` `ar-web` `anim` `toy`
|
|
61
|
-
- `--then` chain steps: `
|
|
71
|
+
- `--then` chain steps: `texture convert import rig-check rig retarget segment complete decimate uv smartsegment`
|
|
62
72
|
- step args: `convert:format=FBX,texture_size=2048` (bare value = primary arg: `convert:fbx`, `decimate:5000`)
|
|
63
|
-
- 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)
|
|
73
|
+
- Output JSON: `{"task_id","type","status","credits_consumed","output_dir","files",["model_file"],["preview"],["normalize_scale"],["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)
|
|
74
|
+
- `normalize_scale` appears when the input was a model file / URL: the import rescaled it (final size = original size × normalize_scale; `1` = untouched) and nothing scales it back — divide by it to recover the original size
|
|
64
75
|
|
|
65
76
|
## Pipes (composing steps yourself)
|
|
66
77
|
|
|
@@ -8,13 +8,18 @@ export TRIPO_API_KEY=tsk_... # zero interaction when a key is already provis
|
|
|
8
8
|
tripo login # humans in a terminal: interactive browser flow
|
|
9
9
|
tripo login --region ov|cn # agents/CI without a key: headless device flow — prints a
|
|
10
10
|
# verification URL + one-time code on stderr, waits for the
|
|
11
|
-
# human to authorize in a browser (≤15 min), saves the key
|
|
11
|
+
# human to authorize in a browser (≤15 min), saves the key;
|
|
12
|
+
# rerunning after an interruption resumes the same code
|
|
13
|
+
tripo login --region ov|cn --no-wait
|
|
14
|
+
# chat agents: print the URL + code and exit at once; rerun
|
|
15
|
+
# it after the human authorizes → "logged_in" (or "pending")
|
|
12
16
|
tripo login --key tsk_... --region ov|cn
|
|
13
17
|
tripo whoami # masked key + active profile + region + balance
|
|
14
18
|
tripo logout # removes the local key of the active profile only
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
Region decides the API host: ov → `openapi.tripo3d.ai`, cn → `openapi.tripo3d.com`.
|
|
22
|
+
`tripo login` probes both regions with the key and stores the right one; a key from `TRIPO_API_KEY` is **not** probed — a China-region key also needs `TRIPO_REGION=cn`.
|
|
18
23
|
|
|
19
24
|
## Multiple accounts (profiles)
|
|
20
25
|
|
|
@@ -35,7 +40,7 @@ tripo topup # opens the billing page, then polls until credits arrive
|
|
|
35
40
|
```
|
|
36
41
|
|
|
37
42
|
Exit code 4 anywhere = insufficient credits → tell the human to run `tripo topup`.
|
|
38
|
-
Failed tasks refund their frozen credits automatically.
|
|
43
|
+
Failed / cancelled tasks refund their frozen credits automatically.
|
|
39
44
|
|
|
40
45
|
## Config
|
|
41
46
|
|
|
@@ -9,6 +9,7 @@ Everything below is the full parameter reference from the official API docs —
|
|
|
9
9
|
tripo generate text-to-model "a ceramic teapot" [--model tripo-v3.1|tripo-p1|tripo-p2] [-p face_limit=15000] ...
|
|
10
10
|
tripo generate image-to-model photo.png [-p texture_alignment=original_image] [-p orientation=align_image]
|
|
11
11
|
tripo generate multiview-to-model front.png back.png [left.png right.png]
|
|
12
|
+
tripo generate multiview-to-model @last # reuse the 4 views of an image-to-multiview / edit-multiview task
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
### P Series (`tripo-p1` / `tripo-p2`) — same endpoints, different model
|
|
@@ -36,7 +37,7 @@ Keywords: model versions · H series (v3.x) · P series (P1 / P2) · Preview ·
|
|
|
36
37
|
| alias (what you type) | wire value (what the API receives) | status | positioning | notes |
|
|
37
38
|
| --- | --- | --- | --- | --- |
|
|
38
39
|
| `tripo-v3.1` (default) | `v3.1-20260211` | GA | latest high-precision; AAA / 3D printing | full feature set |
|
|
39
|
-
| `tripo-p1` | `P1-20260311` | GA | best low-poly; games / UGC / mobile | face_limit
|
|
40
|
+
| `tripo-p1` | `P1-20260311` | GA | best low-poly; games / UGC / mobile | face_limit 50–20000; **no** `quad` `smart_low_poly` `generate_parts` `geometry_quality`; ~2s mesh |
|
|
40
41
|
| `tripo-p2` | `P2-20260801` | **Preview** (2026-08) | P1 upgrade (P series) | adds `quad` (forces FBX); face_limit 48–50000 tri / 48–25000 quad (validated locally); still **no** `smart_low_poly` `generate_parts` `geometry_quality` (stripped with a warning); credits 100 (no texture) / 110 / 120 / 130 for standard / detailed / extreme. Explicit `--model` only — the CLI never auto-selects it |
|
|
41
42
|
| `tripo-v3.0` / `tripo-v2.5` | `v3.0-20250812` / `v2.5-20250123` | Legacy | previous generations | only when the human asks |
|
|
42
43
|
|
|
@@ -56,10 +57,12 @@ Official docs (ov / cn):
|
|
|
56
57
|
| `input` | — | image | `file_` token, `http(s)://` URL or `task_` id (e.g. a text-to-image task) — the CLI uploads local files for you |
|
|
57
58
|
| `inputs` | — | multiview | see "multiview inputs" below |
|
|
58
59
|
| `model_seed` / `image_seed` / `texture_seed` | random | all | geometry / text-to-image stage / texture seeds — the key to reproducibility (`task.json` records them) |
|
|
59
|
-
| `face_limit` | adaptive | all | max polycount; P1
|
|
60
|
+
| `face_limit` | adaptive | all | max polycount; P1 50–20000, P2 48–50000 (quad 48–25000) |
|
|
60
61
|
| `texture` | `true` | all | set `false` for bare geometry (print / re-texture later) — saves credits |
|
|
61
62
|
| `pbr` | `true` | all | PBR maps; **forces `texture=true`** |
|
|
62
|
-
| `texture_quality` | `standard` | all | `standard` / `detailed` / `extreme` (
|
|
63
|
+
| `texture_quality` | `standard` | all | `fast` (texture v3.5 only, same price as standard) / `standard` / `detailed` / `extreme` (8K, extra credits) |
|
|
64
|
+
| `texture_version` | derived from `model` | all | texture model, decoupled from geometry: `v3.5-20260815` (latest) / `v3.0-20250812` (v3.x and P default) / `v2.5-20250123`. Required by `fast` and `delight` — the CLI fills in v3.5 when you omit it; `fast` with another version is rejected locally |
|
|
65
|
+
| `delight` | `true` | all | remove lighting baked into the reference before texturing; `false` keeps the original shading. Texture v3.5 only (older versions ignore it) |
|
|
63
66
|
| `geometry_quality` | `standard` | v3.x | `detailed` = Ultra mode (extra HD credit) |
|
|
64
67
|
| `auto_size` | `false` | all | scale to real-world metres |
|
|
65
68
|
| `quad` | `false` | v3.x, P2 | quad mesh; **forces FBX output** (quads cannot live in GLB) |
|
|
@@ -67,7 +70,7 @@ Official docs (ov / cn):
|
|
|
67
70
|
| `generate_parts` | `false` | v3.x | editable segmented parts — see rules below |
|
|
68
71
|
| `compress` | — | v3.x | `geometry` = meshopt compression |
|
|
69
72
|
| `export_orientation` | `+x` | all | forward axis `+x` / `-x` / `+y` / `-y` — see warning below |
|
|
70
|
-
| `export_uv` | `true` |
|
|
73
|
+
| `export_uv` | `true` | all | `false` = faster, smaller file (UVs are unwrapped at the texture stage instead) |
|
|
71
74
|
| `enable_image_autofix` | `false` | image | auto-repair low-quality inputs |
|
|
72
75
|
| `texture_alignment` | `original_image` | image, multiview | `original_image` (match the picture) / `geometry` (match the mesh) |
|
|
73
76
|
| `orientation` | `default` | image, multiview | `align_image` = align the model to the input viewpoint |
|
|
@@ -88,13 +91,14 @@ The API accepts three `inputs` shapes (never mix them):
|
|
|
88
91
|
- **legacy positional**: exactly 4 strings `[front, left, back, right]`, `""` skips a slot.
|
|
89
92
|
- **task_id**: `[{"task_id":"<image-to-multiview or edit-multiview task>"}]` to reuse a 4-view sheet (source task must be `success`).
|
|
90
93
|
|
|
91
|
-
The CLI builds view-key payloads from your files: filename hints (`front|back|left|right`, or 正面/背面/左/右) win; otherwise positional order front, left, back, right. A front view is mandatory.
|
|
94
|
+
The CLI builds view-key payloads from your files: filename hints (`front|back|left|right`, or 正面/背面/左/右) win; otherwise positional order front, left, back, right. A front view is mandatory. A single task id / `@ref` argument is sent in the task_id shape instead (a `@ref` of another task type is rejected locally).
|
|
92
95
|
|
|
93
96
|
## Images (2D pipeline before 3D)
|
|
94
97
|
|
|
95
98
|
```bash
|
|
96
99
|
tripo generate text-to-image "front view of a knight, T-pose" [-p template=t_pose] [--model seedream_v4]
|
|
97
100
|
tripo generate image-to-image sketch.png --prompt "make it look like clay" [--model seedream_v5]
|
|
101
|
+
tripo generate image-to-image hero.png outfit.png --prompt "the character from image[1] wearing the outfit from image[2]"
|
|
98
102
|
tripo generate image-to-multiview photo.png # 1 image → 4-view sheet
|
|
99
103
|
tripo generate edit-multiview <multiview-task-id> -p 'prompts=[{"view":"front","prompt":"add a sword"}]'
|
|
100
104
|
tripo generate image-to-splat photo.png # gaussian splat
|
|
@@ -106,9 +110,10 @@ tripo generate image-to-splat photo.png # gaussian splat
|
|
|
106
110
|
| --- | --- | --- | --- |
|
|
107
111
|
| `seedream_v4` | ByteDance | GA | text-to-image default (balanced); **not accepted by image-to-image** |
|
|
108
112
|
| `seedream_v5` | ByteDance | GA | image-to-image default; higher quality |
|
|
109
|
-
| `
|
|
110
|
-
| `chat_image_1` / `chat_image_1.5` | OpenAI | retiring 2026-10-23 / 2026-12-01 | GPT image; 1.5 = higher quality — move to `chat_image_2` |
|
|
111
|
-
| `chat_image_2` | OpenAI | GA | GPT
|
|
113
|
+
| `banana` / `banana_pro` / `banana2` | Google | GA | fast / higher quality / latest fast |
|
|
114
|
+
| `chat_image_1` / `chat_image_1.5` | OpenAI | retiring 2026-10-23 / 2026-12-01 | GPT image; 1.5 = higher quality — move to `chat_image_2` or 2.5 |
|
|
115
|
+
| `chat_image_2` | OpenAI | GA | GPT, best quality |
|
|
116
|
+
| `chat_image_2.5_flare` / `chat_image_2.5_sunburst` | OpenAI | GA | 2.5 series: flare = speed tier (chat_image_2 quality, much faster); sunburst = refined tier (keeps unedited areas closest to the original) |
|
|
112
117
|
|
|
113
118
|
Image model ids are sent as-is (no alias normalization). Full list with retirement dates: <https://developers.tripo3d.ai/en/docs/models-and-versions>.
|
|
114
119
|
|
|
@@ -117,15 +122,19 @@ Image model ids are sent as-is (no alias normalization). Full list with retireme
|
|
|
117
122
|
| param | default | endpoint | notes |
|
|
118
123
|
| --- | --- | --- | --- |
|
|
119
124
|
| `prompt` | — | t2i (required), i2i | ≤1024 chars; i2i: required unless `template` is set |
|
|
120
|
-
| `input` | — | i2i, i2mv, edit-mv, splat | `file_` token / URL (i2i also accepts a `task_` id) |
|
|
121
|
-
| `inputs` | — | i2i | multiple reference images: seedream ≤4,
|
|
122
|
-
| `template` | — | t2i, i2i | `asset_extraction` `character_completion` `t_pose` `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
+
| `input` | — | i2i, i2mv, edit-mv, splat | `file_` token / URL (i2i also accepts a `task_` id); i2i: JPEG/PNG ≤20 MB, ≤36 MP |
|
|
126
|
+
| `inputs` | — | i2i | multiple reference images (the CLI sends this when you pass several images): seedream ≤4, banana ≤10, chat_image ≤16; cite them in the prompt as `image[1]`, `image[2]`… |
|
|
127
|
+
| `template` | — | t2i, i2i | t2i: `asset_extraction` `character_completion` `t_pose` `variants` `figure`; i2i: `t_pose` `character_completion` `3d_enhance` `variants` `figure` |
|
|
128
|
+
| `size` | `2048x2048` | t2i, i2i | keyword (`2K` / `4K`) or exact pixels; accepted values differ per model |
|
|
129
|
+
| `quality` | `low` | t2i, i2i | `chat_image_2`: `low` `medium` `high`; `chat_image_2.5_*`: also `xhigh` `max` (no `auto`). Other models reject it. `high` and above cost more credits |
|
|
130
|
+
| `background` | model decides | t2i, i2i | `chat_image_2.5_*` only (ignored elsewhere): `auto` / `opaque` / `transparent`; `transparent` requires `output_format=png` |
|
|
131
|
+
| `aspect_ratio` | `1:1` | t2i, i2i | banana family only: `1:1 2:3 3:2 3:4 4:3 4:5 5:4 9:16 16:9 21:9` (banana2 adds `1:8 1:4 4:1 8:1`); seedream / chat_image use `size` |
|
|
132
|
+
| `output_format` | `png` | t2i, i2i | `png` (lossless, alpha) / `jpeg` (smaller, no alpha) |
|
|
133
|
+
| `watermark` | `false` | t2i | AI-content watermark, seedream only (banana always embeds an invisible one; chat_image has no control) |
|
|
125
134
|
| `model_seed` | random | splat | same seed + same image = repeatable `.splat` |
|
|
126
135
|
|
|
127
|
-
- `template=t_pose`
|
|
128
|
-
- Sizes are billed in 1K / 2K / 4K tiers
|
|
136
|
+
- `template=t_pose` is the golden pre-step for the animation pipeline; `3d_enhance` (i2i) cleans a photo before image-to-model.
|
|
137
|
+
- Sizes are billed in 1K / 2K / 4K tiers.
|
|
129
138
|
- `image-to-multiview` output: `front_view_url` `left_view_url` `back_view_url` `right_view_url` — feed them (or the task id, see above) to `multiview-to-model`.
|
|
130
139
|
- `edit-multiview` takes a successful `image-to-multiview` task; `prompts` is 1–4 `{view, prompt}` objects.
|
|
131
140
|
- `image-to-splat` returns a `.splat` file in `output.model_url` (not a mesh — no texture / rig / convert downstream). Fixed **30 credits**, ~4 minutes. Input PNG / JPEG / WebP ≤20 MB, ≥256×256 px, subject clear and centred. Use image-to-model when you need an editable mesh.
|
package/skill/commands/make.md
CHANGED
|
@@ -34,13 +34,13 @@ In a TTY it then asks `Run this plan?`; answering no exits 0 with `cancelled; no
|
|
|
34
34
|
| quoted text | text-to-model |
|
|
35
35
|
| one image (.png/.jpg/.jpeg/.webp/.bmp) or image URL | image-to-model |
|
|
36
36
|
| 2–4 images | multiview-to-model (filename hints front/back/left/right win; otherwise positional front,left,back,right; front required) |
|
|
37
|
-
| a model file (.glb/.gltf/.fbx/.obj/.stl) | import (add --then for processing) |
|
|
37
|
+
| a model file or model URL (.glb/.gltf/.fbx/.obj/.stl) | import (add --then for processing) |
|
|
38
38
|
| task id / `@last` / `@name` | continue processing from that task (--then required) |
|
|
39
39
|
|
|
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: `
|
|
43
|
+
- `--then <steps>` — processing chain, comma-separated. Steps: `texture 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, `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.
|
|
@@ -69,7 +69,7 @@ tripo make concept.png --for game-mobile --then rig,convert:fbx --dry-run --json
|
|
|
69
69
|
"steps":[
|
|
70
70
|
{"name":"image-to-model","endpoint":"/v3/generation/image-to-model","task_type":"image_to_model",
|
|
71
71
|
"payload":{"input":"<upload:concept.png>","model":"P1-20260311","texture":true,"pbr":true,"texture_quality":"standard","face_limit":15000}},
|
|
72
|
-
{"name":"rig","endpoint":"/v3/animations/rig","task_type":"animate_rig","payload":{"input":"<upstream>","model":"
|
|
72
|
+
{"name":"rig","endpoint":"/v3/animations/rig","task_type":"animate_rig","payload":{"input":"<upstream>","model":"v3.0-20260909"}},
|
|
73
73
|
{"name":"convert","endpoint":"/v3/models/convert","task_type":"convert_model","payload":{"input":"<upstream>","format":"FBX"}}],
|
|
74
74
|
"pending_uploads":["concept.png"],
|
|
75
75
|
"deliverables":["FBX","GLB"],
|
|
@@ -103,6 +103,7 @@ Typical agent loop: dry-run → fix `errors` / adjust params → dry-run again u
|
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
- Single task (no `--then`): `credits_consumed` is that task's spend; no `credits_breakdown`.
|
|
106
|
+
- Model file / URL input: the result adds `normalize_scale`, the factor the import rescaled the model by (final size = original size × normalize_scale; `1` = untouched). The platform never scales it back; divide by it to recover the original size. With `--then`, the steps after the import do not return it, so the result carries the import's value.
|
|
106
107
|
- Chained run: `credits_consumed` is the **total** across generation + every step, and `credits_breakdown` lists each task in run order (generation first). Human mode prints the same as `credits consumed (total): 145` followed by one indented line per task.
|
|
107
108
|
|
|
108
109
|
Exit codes: 0 ok · 2 params · 3 auth · 4 credits · 5 content policy · 6 task failed · 7 network · 9 rate limit.
|