@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.
Files changed (3) hide show
  1. package/README.md +6 -1
  2. package/dist/index.js +113 -13
  3. 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 --agent-version ah_part_extractor_v65
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> --agent-version <id> [--name <name>]
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>...] --agent-version <id> [--part-extraction-mode fast|quality]
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: requireFlag(ctx.flags, 'agent-version'),
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 !== 'quality') {
535
- throw new Error('--part-extraction-mode must be "fast" or "quality"');
628
+ partExtractionMode !== 'high_quality') {
629
+ throw new Error('--part-extraction-mode must be "fast" or "high_quality"');
536
630
  }
537
- print(await ctx.client.v1.executeProduction({
538
- orderId: requireFlag(ctx.flags, 'order-id'),
539
- missionId: requireFlag(ctx.flags, 'mission-id'),
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: requireFlag(ctx.flags, 'agent-version'),
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".');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assethub/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Command line interface for AssetHub.",
5
5
  "type": "module",
6
6
  "bin": {