@assethub/cli 0.1.0 → 0.1.1
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 +6 -1
- package/dist/index.js +113 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ assethub files import https://example.com/input.png --media-type image
|
|
|
11
11
|
assethub image generate --prompt "stylized prop concept" --wait
|
|
12
12
|
assethub mesh generate --file ./input.png --model-id meshGen.hunyuan31 --wait
|
|
13
13
|
assethub jobs watch job_123
|
|
14
|
-
assethub production analyze --image-url https://example.com/input.png --
|
|
14
|
+
assethub production analyze --image-url https://example.com/input.png --part-extractor "V1.5"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Configuration:
|
|
@@ -26,10 +26,15 @@ Agent-friendly login:
|
|
|
26
26
|
printf '%s' "$ASSETHUB_API_KEY" | assethub auth login --api-key-stdin
|
|
27
27
|
assethub image generate --prompt "small stylized game asset chest" --wait --interval-ms 5000 --timeout-ms 900000
|
|
28
28
|
assethub mesh generate --source-url https://example.com/input.png --model-id meshGen.hunyuan31 --wait --interval-ms 5000 --timeout-ms 900000
|
|
29
|
+
assethub production analyze --image-url https://example.com/input.png --part-extractor "V1.5"
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
Image generation defaults to the deployed `v1` endpoint. Pass `--api-version v2`
|
|
32
33
|
only when the `v2` image endpoint is available in the target environment.
|
|
33
34
|
|
|
35
|
+
Part extraction uses the same public names shown in the AssetHub product:
|
|
36
|
+
`V1`, `V1.5 alpha`, `V1.5`, `V2.0 alpha`, and `V2.1 alpha`. Internal agent
|
|
37
|
+
IDs are intentionally not part of the public CLI interface.
|
|
38
|
+
|
|
34
39
|
The CLI only uses AssetHub's public API surface and is designed so this package
|
|
35
40
|
can be published independently from the private application monorepo.
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,52 @@ const defaultBaseUrl = 'https://app.assethub.io';
|
|
|
8
8
|
const defaultImageModelId = 'imageGen.nanoBanana.pro.openrouter';
|
|
9
9
|
const defaultMeshModelId = 'meshGen.hunyuan31';
|
|
10
10
|
const defaultProfileName = 'default';
|
|
11
|
+
const defaultPartExtractorName = 'V1.5';
|
|
12
|
+
const partExtractorOptions = [
|
|
13
|
+
{
|
|
14
|
+
internalId: 'ah_part_extractor_v3',
|
|
15
|
+
publicName: 'Part Extractor V3',
|
|
16
|
+
aliases: ['part extractor v3', 'v3'],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
internalId: 'ah_part_extractor_v5',
|
|
20
|
+
publicName: 'V1',
|
|
21
|
+
aliases: ['v1', '1'],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
internalId: 'ah_part_extractor_v55',
|
|
25
|
+
publicName: 'V1.5 alpha',
|
|
26
|
+
aliases: ['v1.5 alpha', 'v1.5-alpha', '1.5 alpha', '1.5-alpha'],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
internalId: 'ah_part_extractor_v65',
|
|
30
|
+
publicName: 'V1.5',
|
|
31
|
+
aliases: ['v1.5', '1.5', 'latest', 'default'],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
internalId: 'ah_part_extractor_v20_alpha',
|
|
35
|
+
publicName: 'V2.0 alpha',
|
|
36
|
+
aliases: ['v2.0 alpha', 'v2 alpha', 'v2.0-alpha', 'v2-alpha', '2.0 alpha'],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
internalId: 'ah_part_extractor_v21_alpha',
|
|
40
|
+
publicName: 'V2.1 alpha',
|
|
41
|
+
aliases: ['v2.1 alpha', 'v2.1-alpha', '2.1 alpha'],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
const partExtractorPublicNames = partExtractorOptions
|
|
45
|
+
.map(option => option.publicName)
|
|
46
|
+
.join(', ');
|
|
47
|
+
const normalizePartExtractorName = (value) => value.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
48
|
+
const publicNameByInternalPartExtractorId = new Map(partExtractorOptions.map(option => [option.internalId, option.publicName]));
|
|
49
|
+
const internalPartExtractorIdByPublicName = new Map(partExtractorOptions.flatMap(option => [
|
|
50
|
+
[normalizePartExtractorName(option.publicName), option.internalId],
|
|
51
|
+
...option.aliases.map(alias => [
|
|
52
|
+
normalizePartExtractorName(alias),
|
|
53
|
+
option.internalId,
|
|
54
|
+
]),
|
|
55
|
+
]));
|
|
56
|
+
const isInternalPartExtractorId = (value) => value === 'nano_bbox_v1' || value.startsWith('ah_part_extractor_');
|
|
11
57
|
const usage = `AssetHub CLI
|
|
12
58
|
|
|
13
59
|
Usage:
|
|
@@ -25,9 +71,9 @@ Usage:
|
|
|
25
71
|
assethub mesh retopo (--source-resource-id <id> | --source-url <url> | --file-ref-json <json>) --model-id <id> [--wait]
|
|
26
72
|
assethub jobs get <job-id>
|
|
27
73
|
assethub jobs watch <job-id> [--interval-ms <ms>] [--timeout-ms <ms>]
|
|
28
|
-
assethub production analyze --image-url <url> --
|
|
74
|
+
assethub production analyze --image-url <url> [--part-extractor <name>] [--name <name>]
|
|
29
75
|
assethub production status <order-id>
|
|
30
|
-
assethub production execute --order-id <id> --mission-id <id> --task-id <id> [--task-id <id>...] --
|
|
76
|
+
assethub production execute --order-id <id> --mission-id <id> --task-id <id> [--task-id <id>...] [--part-extractor <name>] [--part-extraction-mode fast|high_quality]
|
|
31
77
|
|
|
32
78
|
Global options:
|
|
33
79
|
--api-key <key> Overrides saved auth and ASSETHUB_API_KEY.
|
|
@@ -43,6 +89,7 @@ Examples:
|
|
|
43
89
|
assethub files upload ./input.png --media-type image
|
|
44
90
|
assethub image generate --prompt "stylized prop concept" --wait
|
|
45
91
|
assethub mesh generate --file ./input.png --model-id meshGen.hunyuan31 --wait
|
|
92
|
+
assethub production analyze --image-url https://example.com/input.png --part-extractor "V1.5"
|
|
46
93
|
`;
|
|
47
94
|
const print = (value) => {
|
|
48
95
|
stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
@@ -112,6 +159,41 @@ const requireFlag = (flags, name) => {
|
|
|
112
159
|
}
|
|
113
160
|
return value;
|
|
114
161
|
};
|
|
162
|
+
const getPartExtractorFlag = (flags) => getFlag(flags, 'part-extractor') ??
|
|
163
|
+
getFlag(flags, 'part-extractor-version') ??
|
|
164
|
+
getFlag(flags, 'agent-version');
|
|
165
|
+
const resolvePartExtractorInternalId = (value) => {
|
|
166
|
+
if (isInternalPartExtractorId(value)) {
|
|
167
|
+
throw new Error(`Internal part extractor IDs are not accepted by the public CLI. Use one of: ${partExtractorPublicNames}.`);
|
|
168
|
+
}
|
|
169
|
+
const internalId = internalPartExtractorIdByPublicName.get(normalizePartExtractorName(value));
|
|
170
|
+
if (internalId == null) {
|
|
171
|
+
throw new Error(`Unknown part extractor: ${value}. Use one of: ${partExtractorPublicNames}.`);
|
|
172
|
+
}
|
|
173
|
+
return internalId;
|
|
174
|
+
};
|
|
175
|
+
const resolvePartExtractorForAnalyze = (flags) => resolvePartExtractorInternalId(getPartExtractorFlag(flags) ?? defaultPartExtractorName);
|
|
176
|
+
const publicPartExtractorName = (value) => {
|
|
177
|
+
if (value == null) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return (publicNameByInternalPartExtractorId.get(value) ??
|
|
181
|
+
(isInternalPartExtractorId(value) ? 'Unknown' : value));
|
|
182
|
+
};
|
|
183
|
+
const sanitizeProductionOutput = (value) => {
|
|
184
|
+
if (Array.isArray(value)) {
|
|
185
|
+
return value.map(item => sanitizeProductionOutput(item));
|
|
186
|
+
}
|
|
187
|
+
if (value == null || typeof value !== 'object') {
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
191
|
+
key,
|
|
192
|
+
key === 'agentVersion' && typeof entry === 'string'
|
|
193
|
+
? publicPartExtractorName(entry)
|
|
194
|
+
: sanitizeProductionOutput(entry),
|
|
195
|
+
]));
|
|
196
|
+
};
|
|
115
197
|
const parsePositiveIntegerFlag = (flags, name, fallback) => {
|
|
116
198
|
const raw = getFlag(flags, name);
|
|
117
199
|
if (raw == null) {
|
|
@@ -509,18 +591,30 @@ const commandJobs = async (subcommand, positionals, ctx) => {
|
|
|
509
591
|
}
|
|
510
592
|
throw new Error('Unknown jobs command. Use "jobs get" or "jobs watch".');
|
|
511
593
|
};
|
|
594
|
+
const resolvePartExtractorFromMission = async (ctx, orderId, missionId) => {
|
|
595
|
+
const status = await ctx.client.v1.getProductionStatus(orderId);
|
|
596
|
+
const mission = status.missions.find(item => item.id === missionId);
|
|
597
|
+
if (mission?.agentVersion == null || mission.agentVersion.trim() === '') {
|
|
598
|
+
throw new Error('Could not infer the part extractor from the mission. Pass --part-extractor with one of: ' +
|
|
599
|
+
partExtractorPublicNames);
|
|
600
|
+
}
|
|
601
|
+
if (isInternalPartExtractorId(mission.agentVersion)) {
|
|
602
|
+
return mission.agentVersion;
|
|
603
|
+
}
|
|
604
|
+
return resolvePartExtractorInternalId(mission.agentVersion);
|
|
605
|
+
};
|
|
512
606
|
const commandProduction = async (subcommand, positionals, ctx) => {
|
|
513
607
|
if (subcommand === 'analyze') {
|
|
514
|
-
print(await ctx.client.v1.analyzeProduction({
|
|
608
|
+
print(sanitizeProductionOutput(await ctx.client.v1.analyzeProduction({
|
|
515
609
|
imageUrl: requireFlag(ctx.flags, 'image-url'),
|
|
516
|
-
agentVersion:
|
|
610
|
+
agentVersion: resolvePartExtractorForAnalyze(ctx.flags),
|
|
517
611
|
name: getFlag(ctx.flags, 'name'),
|
|
518
|
-
}));
|
|
612
|
+
})));
|
|
519
613
|
return;
|
|
520
614
|
}
|
|
521
615
|
if (subcommand === 'status') {
|
|
522
616
|
const orderId = requirePositional(positionals, 2, 'order-id');
|
|
523
|
-
print(await ctx.client.v1.getProductionStatus(orderId));
|
|
617
|
+
print(sanitizeProductionOutput(await ctx.client.v1.getProductionStatus(orderId)));
|
|
524
618
|
return;
|
|
525
619
|
}
|
|
526
620
|
if (subcommand === 'execute') {
|
|
@@ -531,16 +625,22 @@ const commandProduction = async (subcommand, positionals, ctx) => {
|
|
|
531
625
|
const partExtractionMode = getFlag(ctx.flags, 'part-extraction-mode');
|
|
532
626
|
if (partExtractionMode != null &&
|
|
533
627
|
partExtractionMode !== 'fast' &&
|
|
534
|
-
partExtractionMode !== '
|
|
535
|
-
throw new Error('--part-extraction-mode must be "fast" or "
|
|
628
|
+
partExtractionMode !== 'high_quality') {
|
|
629
|
+
throw new Error('--part-extraction-mode must be "fast" or "high_quality"');
|
|
536
630
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
631
|
+
const orderId = requireFlag(ctx.flags, 'order-id');
|
|
632
|
+
const missionId = requireFlag(ctx.flags, 'mission-id');
|
|
633
|
+
const partExtractorFlag = getPartExtractorFlag(ctx.flags);
|
|
634
|
+
const agentVersion = partExtractorFlag == null
|
|
635
|
+
? await resolvePartExtractorFromMission(ctx, orderId, missionId)
|
|
636
|
+
: resolvePartExtractorInternalId(partExtractorFlag);
|
|
637
|
+
print(sanitizeProductionOutput(await ctx.client.v1.executeProduction({
|
|
638
|
+
orderId,
|
|
639
|
+
missionId,
|
|
540
640
|
confirmedTaskIds,
|
|
541
|
-
agentVersion
|
|
641
|
+
agentVersion,
|
|
542
642
|
partExtractionMode,
|
|
543
|
-
}));
|
|
643
|
+
})));
|
|
544
644
|
return;
|
|
545
645
|
}
|
|
546
646
|
throw new Error('Unknown production command. Use "production analyze", "production status", or "production execute".');
|