@assethub/cli 0.1.9 → 0.1.10

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
@@ -2,6 +2,37 @@
2
2
 
3
3
  Command line interface for AssetHub.
4
4
 
5
+ ## Connection diagnostics and MCP
6
+
7
+ ```sh
8
+ assethub --version
9
+ assethub doctor --mcp --profile my-workspace
10
+ assethub mcp config --client cursor
11
+ assethub mcp config --client codex
12
+ ```
13
+
14
+ `doctor` checks API access and the authenticated workspace. `--mcp` also checks
15
+ MCP initialization and tool discovery, without invoking any tool or generating
16
+ assets. It returns JSON and exit 0 when checks pass, or exit 2 with a diagnostic
17
+ and next step when a check fails. The default total timeout is 15 seconds; change
18
+ it with `--timeout-ms`. Hosted MCP currently requires Internal access for the key
19
+ creator, independently of regular API access.
20
+
21
+ `mcp config` outputs a Cursor JSON object or Codex TOML section to merge into
22
+ existing user settings. It does not write files or read your saved API key.
23
+ The generated settings read `ASSETHUB_API_KEY` from the AI client's environment.
24
+ Use the same workspace key as your CLI profile. For a different API origin, pass
25
+ `--base-url https://your-host/prefix` (HTTP is accepted only for localhost).
26
+
27
+ Explicit `--profile` uses that profile's key and origin, ahead of environment
28
+ variables, including user tokens and workspace MFA proofs. A missing profile fails.
29
+ Without an explicit profile, a workspace selected with `workspace use` takes
30
+ precedence over environment API credentials; otherwise the environment key
31
+ precedes the saved default. `--api-key` and `--base-url` remain
32
+ explicit overrides. `auth logout` removes the selected saved profile (or the
33
+ saved default); environment credentials remain under your shell’s control.
34
+ Diagnostics omit API keys and raw server error bodies.
35
+
5
36
  ## Agent generation with canvas history
6
37
 
7
38
  This release uses the Internal `api_canvas_execution` gate. Run `assethub capabilities`
@@ -38,7 +69,7 @@ state directory private. `--operation-id <uuid>` supplies an operation identity;
38
69
  retries and `runs resume <operation-id>` reuse its identical body and key. A new
39
70
  candidate needs a new operation ID. `runs watch <run-id>` only reads status.
40
71
 
41
- stdout contains final JSON; stderr contains progress. Exit codes are 0 for accepted/
72
+ stdout contains final JSON; stderr contains progress. Exceptions: `--version` prints the version, and `mcp config` prints the requested configuration. Exit codes are 0 for accepted/
42
73
  completed, 1 for terminal failure/partial, 2 for input/auth/capability/budget errors,
43
74
  3 for timeout/history pending/needs review, and 130 for Ctrl-C. Timeout and Ctrl-C
44
75
  leave server execution running and return known identities.
@@ -393,7 +424,10 @@ assethub composer run --from-run <run-id> --transforms-json @transforms.json --w
393
424
  Selection reuses a valid API key for that workspace or creates one when permitted.
394
425
  Membership, API key creation permissions, and workspace MFA still apply. Complete
395
426
  required MFA in AssetHub and provide its signed proof through
396
- `ASSETHUB_WORKSPACE_MFA`. Generation uses normal workspace credits.
427
+ `ASSETHUB_WORKSPACE_MFA` during user login to save it with the profile. Renew an
428
+ expired proof by logging in again. Explicit `--profile` uses its saved proof and
429
+ ignores environment credentials; an explicit `--api-key` can supply the workspace
430
+ key. Generation uses normal workspace credits.
397
431
 
398
432
  Existing Production outputs can be inspected and downloaded without regenerating:
399
433
 
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import { type AnimationRetargetRequest, type Intervention, type RigRequest } from '@assethub/api-client';
3
3
  import { buildRunUploadPlan } from './runsUpload/buildRunUpload.js';
4
4
  type Flags = Record<string, string | boolean | string[]>;
5
- export declare const usage = "AssetHub CLI\n\nInternal preview: canvas/context/layout and moodboards require workspace feature access.\n\nUsage:\n assethub auth login --api-key <key> [--base-url <url>] [--profile <name>]\n assethub auth login --api-key-stdin [--base-url <url>] [--profile <name>]\n assethub auth login --access-token-stdin [--base-url <url>] [--profile <name>]\n assethub auth status [--profile <name>]\n assethub auth logout [--profile <name>]\n assethub workspace list|get\n assethub workspace create --name <name> [--operation-id <uuid>]\n assethub workspace use <workspace-id>\n assethub workspace prepare|sync|analyze|verify --internal --manifest <json> --state <json>\n assethub workspace self-check --internal\n assethub workspace context put --internal --org <uuid> --actor <uuid> --canvas <id> --file <json> [--if-version <n>] [--order <uuid>]\n assethub workspace context get --internal --org <uuid> --actor <uuid> --canvas <id> [--out <json>] [--order <uuid>]\n assethub project ingest <source-dir> --out-dir <directory> [--exclude-dir <relative-path>...]\n assethub language text|vision --input-json @<json> --operation-id <uuid> [--out <json>]\n assethub canvas import --canvas <id> --file <image> [--name <name>]\n assethub canvas context get --canvas <id> [--out <json>]\n assethub canvas context put --canvas <id> --file <json> [--if-version <n>]\n assethub canvas layout --canvas <id> --input-json @<json>\n assethub canvas compare --canvas <id> --source-id <asset-id> --asset-id <asset-id> --out <html>\n assethub moodboard create --input-json @<json> [--operation-id <uuid>]\n assethub moodboard list|get|update|archive [<board-id>] [--input-json @<json>]\n assethub moodboard analyze <board-id> [--revision <uuid>] [--wait]\n assethub image generate --canvas <id> --context <id> [--context-version <n>] [--context-source <key>] [--moodboard-revision <uuid>] --prompt <text> --wait\n assethub models list [--domain <domain>] [--ids-only] [--limit <n>]\n assethub models get <model-id>\n assethub files import <url> --media-type image|mesh\n assethub files upload <file> --media-type image|mesh\n assethub source create (--file <path> | --source-url <url> | --source-id <id> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard) --media-type image|mesh [--file-name <name>] [--content-type <type>]\n assethub capabilities\n assethub composer models\n assethub composer run --part <mesh-asset-id> [--part <mesh-asset-id>...] --reference <image-asset-id> [--model <id>] [--mode quick|quality] [--transforms-json <json|@file>] [--canvas <id>] [--wait]\n assethub composer run --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]\n assethub composer run --from-run <run-id> [--transforms-json <json|@file>] [--mode quick|quality] [--wait]\n assethub canvas create [--name <name>] [--operation-id <uuid>]\n assethub canvas list [--cursor <cursor>] [--limit <n>]\n assethub canvas get|use|open [<id>]\n assethub runs list [--canvas <id>] [--cursor <cursor>] [--limit <n>]\n assethub runs get|watch <run-id> [--timeout-ms <n>]\n assethub runs resume <operation-id> [--wait]\n assethub graph show|export [--canvas <id>] [--artifact <node-id>] [--out <path>] [--allow-truncated]\n assethub graph lineage --artifact <node-id> [--canvas <id>] [--direction ancestors|descendants|both] [--depth <n>]\n assethub evaluate list\n assethub evaluations submit --artifact <asset-id> --report <json|file|@file|@-> [--agent <name>] [--canvas <id>] [--reference <asset-id>...] [--run <run-id>] [--operation-id <uuid>] [--require-pass]\n assethub evaluations list [--canvas <id>] [--artifact <asset-id>]\n assethub evaluations get <evaluation-id>\n assethub image generate --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--agent <name>] [--derived-from-run <run-id>] [--from-node <graphId/nodeId> --graph-revision <rev>] [--wait]\n assethub mesh generate --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--agent <name>] [--derived-from-run <run-id>] [--from-node <graphId/nodeId> --graph-revision <rev>] [--wait]\n assethub image generate --prompt <text> [--file <path> | --source-url <url> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard] [--file-name <name>] [--content-type <type>] [--model-id <id>] [--batch-size <n>] [--wait] [--download --out-dir <dir>] [--canvas <id>] [--operation-id <uuid>] [--api-version v2]\n assethub image generate --prompt <text> --source-resource-id <id> --api-version v2 [--model-id <id>] [--batch-size <n>] [--wait]\n assethub mesh generate (--file <path> | --source-url <url> | --source-resource-id <id> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) [--file-name <name>] [--content-type <type>] [--model-id <id>] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub mesh retopo (--source-resource-id <id> | --source-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) --model-id <id> [--file-name <name>] [--content-type <type>] [--wait] [--download --out-dir <dir>]\n assethub rig check (--file <path> | --source-url <url> | --source-resource-id <id> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub rig create (--file <path> | --source-url <url> | --source-resource-id <id> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) [--model v1.0-20240301|v2.5-20260210] [--rig-type biped|quadruped|hexapod|octopod|avian|serpentine|aquatic] [--spec tripo|mixamo] [--out-format glb|fbx] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub animate presets [--model v1.0-20240301|v2.5-20260210] [--rig-type <type>]\n assethub animate retarget --resource-id <id> --animation <preset-id> [--animation <preset-id>... up to 5 total] [--out-format glb|fbx] [--bake-animation true|false] [--export-with-geometry true|false] [--animate-in-place true|false] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub jobs get <job-id> [--download --out-dir <dir>]\n assethub jobs watch <job-id> [--interval-ms <ms>] [--timeout-ms <ms>] [--download --out-dir <dir>]\n assethub production analyze (--file <path> | --source-url <url> | --source-id <id> | --image-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard) [--file-name <name>] [--content-type <type>] [--part-extractor <name>] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub production agents\n assethub production automation --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--wait]\n assethub production status <order-id> [--wait] [--download --out-dir <dir>]\n assethub production execute --order-id <id> --mission-id <id> --task-id <id> [--task-id <id>...] [--part-extractor <name>] [--part-extraction-mode fast|high_quality] [--wait] [--download --out-dir <dir>]\n assethub production run --order-id <id> --mission-id <id> [--run-mode full_auto|approval] [--task-id <id>...] [--part-extractor <name>] [--part-extraction-mode fast|high_quality] [--allowed-model-id <id>...] [--max-iterations <n>] [--max-cost-credits <n>] [--wait] [--download --out-dir <dir>]\n assethub production watch <order-id> [--interval-ms <ms>] [--timeout-ms <ms>] [--download --out-dir <dir>]\n assethub production intervene <order-id> (--exclude <target-id> | --include <target-id> | --add-part <name> | --rename <target-id>=<name> | --reject <target-id>[=<reason>] | --regenerate <target-id>=<mode> | --set-param <key>=<value> | --ops-json <json|@file|@->)... [--idempotency-key <key>]\n assethub production interventions <order-id>\n assethub runs upload <path> [--graph-id <id>] [--stream-id <id>] [--rev <n>] [--description <text>] [--tag <tag>...] [--skip-register] [--dry-run]\n assethub parts split (--file <path> | --source-url <url> | --source-id <id> | --image-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard | --order-id <id>) [--file-name <name>] [--content-type <type>] [--part-extractor <name>] [--wait] [--task-id <id>...] [--mission-id <id>] [--all-ready] [--download --out-dir <dir>]\n assethub parts compare (--file <path> | --source-url <url> | --source-id <id> | --image-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard) [--file-name <name>] [--content-type <type>] [--preprocess-prompt <text>] [--preprocess-model-id <id>] [--fail-on-preprocess-error] [--part-extractor <name>] [--wait] [--task-id <id>...] [--mission-id <id>] [--all-ready] [--download --out-dir <dir>]\n assethub autopilot [<image>] --demo [--yolo] [--json] [--max-regen <n>] [--stall-rounds <n>] [--tie-epsilon <x>] [--multiview 2-view|4-view|6-view] [--ab-mode serial|cross] [--credit-budget <n>]\n\nGlobal options:\n --api-key <key> Overrides saved auth and ASSETHUB_API_KEY.\n --base-url <url> Defaults to ASSETHUB_API_BASE_URL, saved auth, or https://app.assethub.io.\n --profile <name> Saved auth profile name. Defaults to default.\n --config <path> Auth config path. Defaults to ~/.assethub/config.json.\n --help Show help.\n\nExamples:\n ASSETHUB_API_KEY=ah_live_xxx assethub models list\n assethub auth login --api-key-stdin\n assethub models list --ids-only\n assethub files upload ./input.png --media-type image\n assethub source create --file ./input.png --media-type image\n pbpaste | assethub source create --stdin-data-uri --media-type image\n assethub source create --clipboard --media-type image --file-name clipboard.png\n printf '{\"image_url\":{\"url\":\"data:image/png;base64,...\"}}' | assethub parts split --stdin-json --part-extractor \"V1.5\" --wait\n base64 -i ./input.png | assethub parts split --stdin-base64 --file-name input.png --content-type image/png --part-extractor \"V1.5\" --wait\n cat ./input.png | assethub parts split --stdin --file-name input.png --part-extractor \"V1.5\" --wait\n assethub image generate --prompt \"stylized prop concept\" --wait\n assethub mesh generate --file ./input.png --wait\n assethub animate presets --rig-type biped\n assethub rig create --source-resource-id mesh_x --rig-type biped --wait\n assethub animate retarget --resource-id rig_x --animation preset:idle --wait\n assethub production analyze --file ./input.png --part-extractor \"V1.5\" --wait\n assethub production run --order-id ord_x --mission-id ms_x --run-mode full_auto --max-cost-credits 200 --wait\n assethub production watch ord_x\n assethub production intervene ord_x --exclude 7 --rename 3=\"left wing\" --idempotency-key review-2026-07-29\n assethub production interventions ord_x\n assethub runs upload ./out/pluffy-run-2026-07-30 --dry-run\n assethub runs upload ./out/pluffy-run-2026-07-30 --tag pluffy --description \"V3 humanoid, 18 parts\"\n assethub parts split --file ./input.png --part-extractor \"V1.5\" --wait --all-ready\n assethub parts compare --file ./input.png --preprocess-prompt \"clean white background, centered product photo\" --part-extractor \"V1.5\" --wait --all-ready\n";
5
+ export declare const usage = "AssetHub CLI\n\nInternal preview: canvas/context/layout and moodboards require workspace feature access.\n\nUsage:\n assethub --version\n assethub doctor [--mcp] [--profile <name>] [--timeout-ms <n>]\n assethub mcp config --client cursor|codex [--base-url <url>]\n assethub auth login --api-key <key> [--base-url <url>] [--profile <name>]\n assethub auth login --api-key-stdin [--base-url <url>] [--profile <name>]\n assethub auth login --access-token-stdin [--base-url <url>] [--profile <name>]\n assethub auth status [--profile <name>]\n assethub auth logout [--profile <name>]\n assethub workspace list|get\n assethub workspace create --name <name> [--operation-id <uuid>]\n assethub workspace use <workspace-id>\n assethub workspace prepare|sync|analyze|verify --internal --manifest <json> --state <json>\n assethub workspace self-check --internal\n assethub workspace context put --internal --org <uuid> --actor <uuid> --canvas <id> --file <json> [--if-version <n>] [--order <uuid>]\n assethub workspace context get --internal --org <uuid> --actor <uuid> --canvas <id> [--out <json>] [--order <uuid>]\n assethub project ingest <source-dir> --out-dir <directory> [--exclude-dir <relative-path>...]\n assethub language text|vision --input-json @<json> --operation-id <uuid> [--out <json>]\n assethub canvas import --canvas <id> --file <image> [--name <name>]\n assethub canvas context get --canvas <id> [--out <json>]\n assethub canvas context put --canvas <id> --file <json> [--if-version <n>]\n assethub canvas layout --canvas <id> --input-json @<json>\n assethub canvas compare --canvas <id> --source-id <asset-id> --asset-id <asset-id> --out <html>\n assethub moodboard create --input-json @<json> [--operation-id <uuid>]\n assethub moodboard list|get|update|archive [<board-id>] [--input-json @<json>]\n assethub moodboard analyze <board-id> [--revision <uuid>] [--wait]\n assethub image generate --canvas <id> --context <id> [--context-version <n>] [--context-source <key>] [--moodboard-revision <uuid>] --prompt <text> --wait\n assethub models list [--domain <domain>] [--ids-only] [--limit <n>]\n assethub models get <model-id>\n assethub files import <url> --media-type image|mesh\n assethub files upload <file> --media-type image|mesh\n assethub source create (--file <path> | --source-url <url> | --source-id <id> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard) --media-type image|mesh [--file-name <name>] [--content-type <type>]\n assethub capabilities\n assethub composer models\n assethub composer run --part <mesh-asset-id> [--part <mesh-asset-id>...] --reference <image-asset-id> [--model <id>] [--mode quick|quality] [--transforms-json <json|@file>] [--canvas <id>] [--wait]\n assethub composer run --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]\n assethub composer run --from-run <run-id> [--transforms-json <json|@file>] [--mode quick|quality] [--wait]\n assethub canvas create [--name <name>] [--operation-id <uuid>]\n assethub canvas list [--cursor <cursor>] [--limit <n>]\n assethub canvas get|use|open [<id>]\n assethub runs list [--canvas <id>] [--cursor <cursor>] [--limit <n>]\n assethub runs get|watch <run-id> [--timeout-ms <n>]\n assethub runs resume <operation-id> [--wait]\n assethub graph show|export [--canvas <id>] [--artifact <node-id>] [--out <path>] [--allow-truncated]\n assethub graph lineage --artifact <node-id> [--canvas <id>] [--direction ancestors|descendants|both] [--depth <n>]\n assethub evaluate list\n assethub evaluations submit --artifact <asset-id> --report <json|file|@file|@-> [--agent <name>] [--canvas <id>] [--reference <asset-id>...] [--run <run-id>] [--operation-id <uuid>] [--require-pass]\n assethub evaluations list [--canvas <id>] [--artifact <asset-id>]\n assethub evaluations get <evaluation-id>\n assethub image generate --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--agent <name>] [--derived-from-run <run-id>] [--from-node <graphId/nodeId> --graph-revision <rev>] [--wait]\n assethub mesh generate --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--agent <name>] [--derived-from-run <run-id>] [--from-node <graphId/nodeId> --graph-revision <rev>] [--wait]\n assethub image generate --prompt <text> [--file <path> | --source-url <url> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard] [--file-name <name>] [--content-type <type>] [--model-id <id>] [--batch-size <n>] [--wait] [--download --out-dir <dir>] [--canvas <id>] [--operation-id <uuid>] [--api-version v2]\n assethub image generate --prompt <text> --source-resource-id <id> --api-version v2 [--model-id <id>] [--batch-size <n>] [--wait]\n assethub mesh generate (--file <path> | --source-url <url> | --source-resource-id <id> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) [--file-name <name>] [--content-type <type>] [--model-id <id>] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub mesh retopo (--source-resource-id <id> | --source-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) --model-id <id> [--file-name <name>] [--content-type <type>] [--wait] [--download --out-dir <dir>]\n assethub rig check (--file <path> | --source-url <url> | --source-resource-id <id> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub rig create (--file <path> | --source-url <url> | --source-resource-id <id> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri>) [--model v1.0-20240301|v2.5-20260210] [--rig-type biped|quadruped|hexapod|octopod|avian|serpentine|aquatic] [--spec tripo|mixamo] [--out-format glb|fbx] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub animate presets [--model v1.0-20240301|v2.5-20260210] [--rig-type <type>]\n assethub animate retarget --resource-id <id> --animation <preset-id> [--animation <preset-id>... up to 5 total] [--out-format glb|fbx] [--bake-animation true|false] [--export-with-geometry true|false] [--animate-in-place true|false] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub jobs get <job-id> [--download --out-dir <dir>]\n assethub jobs watch <job-id> [--interval-ms <ms>] [--timeout-ms <ms>] [--download --out-dir <dir>]\n assethub production analyze (--file <path> | --source-url <url> | --source-id <id> | --image-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard) [--file-name <name>] [--content-type <type>] [--part-extractor <name>] [--name <name>] [--wait] [--download --out-dir <dir>]\n assethub production agents\n assethub production automation --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--wait]\n assethub production status <order-id> [--wait] [--download --out-dir <dir>]\n assethub production execute --order-id <id> --mission-id <id> --task-id <id> [--task-id <id>...] [--part-extractor <name>] [--part-extraction-mode fast|high_quality] [--wait] [--download --out-dir <dir>]\n assethub production run --order-id <id> --mission-id <id> [--run-mode full_auto|approval] [--task-id <id>...] [--part-extractor <name>] [--part-extraction-mode fast|high_quality] [--allowed-model-id <id>...] [--max-iterations <n>] [--max-cost-credits <n>] [--wait] [--download --out-dir <dir>]\n assethub production watch <order-id> [--interval-ms <ms>] [--timeout-ms <ms>] [--download --out-dir <dir>]\n assethub production intervene <order-id> (--exclude <target-id> | --include <target-id> | --add-part <name> | --rename <target-id>=<name> | --reject <target-id>[=<reason>] | --regenerate <target-id>=<mode> | --set-param <key>=<value> | --ops-json <json|@file|@->)... [--idempotency-key <key>]\n assethub production interventions <order-id>\n assethub runs upload <path> [--graph-id <id>] [--stream-id <id>] [--rev <n>] [--description <text>] [--tag <tag>...] [--skip-register] [--dry-run]\n assethub parts split (--file <path> | --source-url <url> | --source-id <id> | --image-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard | --order-id <id>) [--file-name <name>] [--content-type <type>] [--part-extractor <name>] [--wait] [--task-id <id>...] [--mission-id <id>] [--all-ready] [--download --out-dir <dir>]\n assethub parts compare (--file <path> | --source-url <url> | --source-id <id> | --image-url <url> | --file-ref-json <json> | --stdin | --stdin-base64 | --stdin-data-uri | --stdin-json | --source-json <json|@file|@-> | --data-uri <uri> | --clipboard) [--file-name <name>] [--content-type <type>] [--preprocess-prompt <text>] [--preprocess-model-id <id>] [--fail-on-preprocess-error] [--part-extractor <name>] [--wait] [--task-id <id>...] [--mission-id <id>] [--all-ready] [--download --out-dir <dir>]\n assethub autopilot [<image>] --demo [--yolo] [--json] [--max-regen <n>] [--stall-rounds <n>] [--tie-epsilon <x>] [--multiview 2-view|4-view|6-view] [--ab-mode serial|cross] [--credit-budget <n>]\n\nGlobal options:\n --api-key <key> Overrides saved auth and ASSETHUB_API_KEY.\n --base-url <url> Override the selected API origin. Default: https://app.assethub.io.\n --profile <name> Select this saved profile ahead of environment credentials and origin.\n --config <path> Auth config path. Defaults to ~/.assethub/config.json.\n --version Show installed package version.\n --help Show help.\n\nExamples:\n ASSETHUB_API_KEY=ah_live_xxx assethub models list\n assethub auth login --api-key-stdin\n assethub models list --ids-only\n assethub files upload ./input.png --media-type image\n assethub source create --file ./input.png --media-type image\n pbpaste | assethub source create --stdin-data-uri --media-type image\n assethub source create --clipboard --media-type image --file-name clipboard.png\n printf '{\"image_url\":{\"url\":\"data:image/png;base64,...\"}}' | assethub parts split --stdin-json --part-extractor \"V1.5\" --wait\n base64 -i ./input.png | assethub parts split --stdin-base64 --file-name input.png --content-type image/png --part-extractor \"V1.5\" --wait\n cat ./input.png | assethub parts split --stdin --file-name input.png --part-extractor \"V1.5\" --wait\n assethub image generate --prompt \"stylized prop concept\" --wait\n assethub mesh generate --file ./input.png --wait\n assethub animate presets --rig-type biped\n assethub rig create --source-resource-id mesh_x --rig-type biped --wait\n assethub animate retarget --resource-id rig_x --animation preset:idle --wait\n assethub production analyze --file ./input.png --part-extractor \"V1.5\" --wait\n assethub production run --order-id ord_x --mission-id ms_x --run-mode full_auto --max-cost-credits 200 --wait\n assethub production watch ord_x\n assethub production intervene ord_x --exclude 7 --rename 3=\"left wing\" --idempotency-key review-2026-07-29\n assethub production interventions ord_x\n assethub runs upload ./out/pluffy-run-2026-07-30 --dry-run\n assethub runs upload ./out/pluffy-run-2026-07-30 --tag pluffy --description \"V3 humanoid, 18 parts\"\n assethub parts split --file ./input.png --part-extractor \"V1.5\" --wait --all-ready\n assethub parts compare --file ./input.png --preprocess-prompt \"clean white background, centered product photo\" --part-extractor \"V1.5\" --wait --all-ready\n";
6
6
  /**
7
7
  * The `animate retarget` request, built from flags alone.
8
8
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAqBA,OAAO,EAIL,KAAK,wBAAwB,EAO7B,KAAK,YAAY,EAEjB,KAAK,UAAU,EAYhB,MAAM,sBAAsB,CAAA;AAa7B,OAAO,EAAC,kBAAkB,EAAc,MAAM,gCAAgC,CAAA;AAI9E,KAAK,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;AA8HxD,eAAO,MAAM,KAAK,+/WA4GjB,CAAA;AAsnED;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GACxC,OAAO,KAAK,KACX,wBA0BF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,OAAO,KAAK,KACX,IAAI,CAAC,UAAU,EAAE,QAAQ,CAM1B,CAAA;AA6fF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,SAAS,MAAM,EAAE,KACtB,YAAY,EAuGd,CAAA;AA8oCD,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,YAAY,MAAM,GAAG,SAAS,EAC9B,aAAa,MAAM,EAAE,EACrB,OAAO,KAAK,KACX,cAoBF,CAAA;AAED,KAAK,iBAAiB,GAAG,IAAI,CAC3B,OAAO,CAAC,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,EAC5C,SAAS,GACT,UAAU,GACV,KAAK,GACL,WAAW,GACX,QAAQ,GACR,OAAO,GACP,OAAO,GACP,UAAU,CACb,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,WAAW,MAAM,EACjB,MAAM,iBAAiB;;;;;;;;;;;;;;CAUvB,CAAA;AAEF,eAAO,MAAM,sBAAsB,GACjC,SAAS,UAAU,CAAC,OAAO,iBAAiB,CAAC,EAC7C,MAAM,iBAAiB,EACvB,SAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;CAYf,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAsBA,OAAO,EAIL,KAAK,wBAAwB,EAO7B,KAAK,YAAY,EAEjB,KAAK,UAAU,EAYhB,MAAM,sBAAsB,CAAA;AAa7B,OAAO,EAAC,kBAAkB,EAAc,MAAM,gCAAgC,CAAA;AAI9E,KAAK,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;AA8HxD,eAAO,MAAM,KAAK,iuXAgHjB,CAAA;AAimED;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GACxC,OAAO,KAAK,KACX,wBA0BF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,OAAO,KAAK,KACX,IAAI,CAAC,UAAU,EAAE,QAAQ,CAM1B,CAAA;AA6fF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,SAAS,MAAM,EAAE,KACtB,YAAY,EAuGd,CAAA;AA8oCD,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,YAAY,MAAM,GAAG,SAAS,EAC9B,aAAa,MAAM,EAAE,EACrB,OAAO,KAAK,KACX,cAoBF,CAAA;AAED,KAAK,iBAAiB,GAAG,IAAI,CAC3B,OAAO,CAAC,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,EAC5C,SAAS,GACT,UAAU,GACV,KAAK,GACL,WAAW,GACX,QAAQ,GACR,OAAO,GACP,OAAO,GACP,UAAU,CACb,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,WAAW,MAAM,EACjB,MAAM,iBAAiB;;;;;;;;;;;;;;CAUvB,CAAA;AAEF,eAAO,MAAM,sBAAsB,GACjC,SAAS,UAAU,CAAC,OAAO,iBAAiB,CAAC,EAC7C,MAAM,iBAAiB,EACvB,SAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;CAYf,CAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { setTimeout as delay } from 'node:timers/promises';
3
+ import { cliVersion, diagnose, mcpConfig } from './setup.js';
3
4
  import { ingestProjectSources } from './projectSources.js';
4
5
  import { writeCanvasComparison } from './canvasComparison.js';
5
6
  import { importCanvasAssetWithState } from './canvasAssetImport.js';
@@ -66,6 +67,9 @@ export const usage = `AssetHub CLI
66
67
  Internal preview: canvas/context/layout and moodboards require workspace feature access.
67
68
 
68
69
  Usage:
70
+ assethub --version
71
+ assethub doctor [--mcp] [--profile <name>] [--timeout-ms <n>]
72
+ assethub mcp config --client cursor|codex [--base-url <url>]
69
73
  assethub auth login --api-key <key> [--base-url <url>] [--profile <name>]
70
74
  assethub auth login --api-key-stdin [--base-url <url>] [--profile <name>]
71
75
  assethub auth login --access-token-stdin [--base-url <url>] [--profile <name>]
@@ -139,9 +143,10 @@ Usage:
139
143
 
140
144
  Global options:
141
145
  --api-key <key> Overrides saved auth and ASSETHUB_API_KEY.
142
- --base-url <url> Defaults to ASSETHUB_API_BASE_URL, saved auth, or ${defaultBaseUrl}.
143
- --profile <name> Saved auth profile name. Defaults to ${defaultProfileName}.
146
+ --base-url <url> Override the selected API origin. Default: ${defaultBaseUrl}.
147
+ --profile <name> Select this saved profile ahead of environment credentials and origin.
144
148
  --config <path> Auth config path. Defaults to ~/.assethub/config.json.
149
+ --version Show installed package version.
145
150
  --help Show help.
146
151
 
147
152
  Examples:
@@ -339,7 +344,7 @@ const defaultFileNameForMediaType = (mediaType) => mediaType === 'image' ? 'stdi
339
344
  const getSourceResourceIdFlag = (flags) => getFlag(flags, 'source-resource-id') ??
340
345
  getFlag(flags, 'source-id') ??
341
346
  getFlag(flags, 'resource-id');
342
- const getProfileName = (flags) => getFlag(flags, 'profile') ?? defaultProfileName;
347
+ const getProfileName = (flags) => hasFlag(flags, 'profile') ? requireFlag(flags, 'profile') : defaultProfileName;
343
348
  const getConfigPath = (flags) => getFlag(flags, 'config') ??
344
349
  env.ASSETHUB_CLI_CONFIG ??
345
350
  join(homedir(), '.assethub', 'config.json');
@@ -394,49 +399,32 @@ const readStdinBlob = async (type) => await new Promise((resolve, reject) => {
394
399
  stdin.on('error', reject);
395
400
  });
396
401
  const resolveAuth = async (flags) => {
397
- const profile = getProfileName(flags);
398
- const baseUrlFromFlags = getFlag(flags, 'base-url');
399
- const apiKeyFromFlags = getFlag(flags, 'api-key');
400
- if (apiKeyFromFlags != null && apiKeyFromFlags !== 'true') {
401
- return {
402
- apiKey: apiKeyFromFlags,
403
- baseUrl: baseUrlFromFlags ?? env.ASSETHUB_API_BASE_URL ?? defaultBaseUrl,
404
- profile,
405
- source: 'flag',
406
- };
407
- }
408
- const config = await readAuthConfig(getConfigPath(flags));
409
- const storedName = getFlag(flags, 'profile') ?? config.defaultProfile ?? profile;
410
- const storedProfile = config.profiles?.[storedName];
411
- const storedAuth = storedProfile?.apiKey
412
- ? {
413
- apiKey: storedProfile.apiKey,
414
- baseUrl: baseUrlFromFlags ??
415
- env.ASSETHUB_API_BASE_URL ??
416
- storedProfile.baseUrl,
417
- profile: storedName,
418
- source: 'profile',
419
- }
402
+ const explicitProfile = hasFlag(flags, 'profile') ? requireFlag(flags, 'profile') : undefined;
403
+ const overrideKey = hasFlag(flags, 'api-key') ? requireFlag(flags, 'api-key') : undefined;
404
+ const config = !overrideKey || explicitProfile
405
+ ? await readAuthConfig(getConfigPath(flags))
420
406
  : undefined;
421
- // An explicit profile or workspace selection must control the next command,
422
- // even if the shell still contains the previous organization's key.
423
- if (storedAuth && (getFlag(flags, 'profile') || storedProfile?.workspaceId)) {
424
- if (storedProfile?.workspaceId &&
425
- new URL(storedAuth.baseUrl).origin !==
426
- new URL(storedProfile.baseUrl).origin)
407
+ const profile = explicitProfile ?? config?.defaultProfile ?? defaultProfileName;
408
+ const storedProfile = config?.profiles?.[profile];
409
+ const selected = Boolean(explicitProfile || storedProfile?.workspaceId);
410
+ if (selected && (!storedProfile || (!storedProfile.apiKey && !overrideKey)))
411
+ throw new Error('Selected profile has no workspace API key. Use workspace use <id> or auth login --api-key-stdin --profile <name>.');
412
+ const baseUrl = getFlag(flags, 'base-url') ??
413
+ (selected
414
+ ? storedProfile.baseUrl
415
+ : env.ASSETHUB_API_BASE_URL ?? storedProfile?.baseUrl ?? defaultBaseUrl);
416
+ if (overrideKey)
417
+ return { apiKey: overrideKey, baseUrl, profile, source: 'flag' };
418
+ if (selected && storedProfile?.apiKey) {
419
+ if (storedProfile.workspaceId &&
420
+ new URL(baseUrl).origin !== new URL(storedProfile.baseUrl).origin)
427
421
  throw new Error('Selected workspace belongs to another API origin; select a workspace for this origin or supply an explicit API key');
428
- return storedAuth;
422
+ return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile' };
429
423
  }
430
- if (env.ASSETHUB_API_KEY != null && env.ASSETHUB_API_KEY.trim() !== '') {
431
- return {
432
- apiKey: env.ASSETHUB_API_KEY,
433
- baseUrl: baseUrlFromFlags ?? env.ASSETHUB_API_BASE_URL ?? defaultBaseUrl,
434
- profile,
435
- source: 'env',
436
- };
437
- }
438
- if (storedAuth)
439
- return storedAuth;
424
+ if (env.ASSETHUB_API_KEY?.trim())
425
+ return { apiKey: env.ASSETHUB_API_KEY, baseUrl, profile, source: 'env' };
426
+ if (storedProfile?.apiKey)
427
+ return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile' };
440
428
  throw new Error('Missing workspace API key. Use "workspace use <id>" after user login, "auth login --api-key-stdin", or ASSETHUB_API_KEY.');
441
429
  };
442
430
  const createContext = async (flags, args) => {
@@ -1230,20 +1218,21 @@ const commandModels = async (subcommand, positionals, ctx) => {
1230
1218
  const workspaceAuth = async (flags) => {
1231
1219
  const configPath = getConfigPath(flags);
1232
1220
  const config = await readAuthConfig(configPath);
1233
- const profile = getFlag(flags, 'profile') ?? config.defaultProfile ?? defaultProfileName;
1221
+ const explicitProfile = hasFlag(flags, 'profile') ? requireFlag(flags, 'profile') : undefined;
1222
+ const profile = explicitProfile ?? config.defaultProfile ?? defaultProfileName;
1234
1223
  const stored = config.profiles?.[profile];
1235
- const accessToken = env.ASSETHUB_ACCESS_TOKEN?.trim() || stored?.accessToken;
1224
+ const environmentToken = explicitProfile ? undefined : env.ASSETHUB_ACCESS_TOKEN?.trim();
1225
+ const accessToken = environmentToken || stored?.accessToken;
1236
1226
  if (!accessToken)
1237
1227
  throw new Error('Workspace management requires user authentication: auth login --access-token-stdin or ASSETHUB_ACCESS_TOKEN');
1238
1228
  const baseUrl = getFlag(flags, 'base-url') ??
1239
- env.ASSETHUB_API_BASE_URL ??
1240
- stored?.baseUrl ??
1229
+ (explicitProfile ? stored?.baseUrl : env.ASSETHUB_API_BASE_URL ?? stored?.baseUrl) ??
1241
1230
  defaultBaseUrl;
1242
- if (!env.ASSETHUB_ACCESS_TOKEN?.trim() &&
1231
+ if (!environmentToken &&
1243
1232
  stored &&
1244
1233
  new URL(baseUrl).origin !== new URL(stored.baseUrl).origin)
1245
1234
  throw new Error('Saved user authentication belongs to another API origin; log in for this origin');
1246
- const workspaceMfaToken = env.ASSETHUB_WORKSPACE_MFA?.trim() ||
1235
+ const workspaceMfaToken = (!explicitProfile && env.ASSETHUB_WORKSPACE_MFA?.trim()) ||
1247
1236
  (stored && new URL(baseUrl).origin === new URL(stored.baseUrl).origin
1248
1237
  ? stored.workspaceMfaToken
1249
1238
  : undefined);
@@ -1258,6 +1247,11 @@ const workspaceAuth = async (flags) => {
1258
1247
  };
1259
1248
  };
1260
1249
  const commandWorkspace = async (subcommand, positionals, flags) => {
1250
+ const suppliedKey = hasFlag(flags, 'api-key')
1251
+ ? requireFlag(flags, 'api-key')
1252
+ : hasFlag(flags, 'profile')
1253
+ ? undefined
1254
+ : env.ASSETHUB_API_KEY?.trim();
1261
1255
  const auth = await workspaceAuth(flags);
1262
1256
  if (subcommand === 'list') {
1263
1257
  print(await auth.client.list());
@@ -1301,7 +1295,6 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
1301
1295
  new URL(entry.baseUrl).origin === new URL(auth.baseUrl).origin &&
1302
1296
  entry.apiKey);
1303
1297
  let apiKey;
1304
- const suppliedKey = getFlag(flags, 'api-key') ?? env.ASSETHUB_API_KEY?.trim();
1305
1298
  for (const candidate of new Set([cached?.[1].apiKey, suppliedKey])) {
1306
1299
  if (!candidate)
1307
1300
  continue;
@@ -1431,7 +1424,7 @@ const commandAuth = async (subcommand, flags) => {
1431
1424
  if (subcommand === 'status') {
1432
1425
  const config = await readAuthConfig(configPath);
1433
1426
  const current = config.profiles?.[getFlag(flags, 'profile') ?? config.defaultProfile ?? profile];
1434
- if (env.ASSETHUB_ACCESS_TOKEN?.trim() ||
1427
+ if ((!hasFlag(flags, 'profile') && env.ASSETHUB_ACCESS_TOKEN?.trim()) ||
1435
1428
  (current?.accessToken && !current.apiKey)) {
1436
1429
  const auth = await workspaceAuth(flags);
1437
1430
  const account = await auth.client.list();
@@ -1462,22 +1455,15 @@ const commandAuth = async (subcommand, flags) => {
1462
1455
  }
1463
1456
  if (subcommand === 'logout') {
1464
1457
  const config = await readAuthConfig(configPath);
1465
- const profiles = config.profiles ?? {};
1466
1458
  const selectedProfile = getFlag(flags, 'profile') ?? config.defaultProfile ?? profile;
1459
+ const profiles = config.profiles ?? {};
1467
1460
  const existed = profiles[selectedProfile] != null;
1468
1461
  delete profiles[selectedProfile];
1469
1462
  await writeAuthConfig(configPath, {
1470
- defaultProfile: config.defaultProfile === selectedProfile
1471
- ? undefined
1472
- : config.defaultProfile,
1463
+ defaultProfile: config.defaultProfile === selectedProfile ? undefined : config.defaultProfile,
1473
1464
  profiles,
1474
1465
  });
1475
- print({
1476
- success: true,
1477
- profile: selectedProfile,
1478
- removed: existed,
1479
- configPath,
1480
- });
1466
+ print({ success: true, profile: selectedProfile, removed: existed, configPath });
1481
1467
  return;
1482
1468
  }
1483
1469
  throw new Error('Unknown auth command. Use "auth login", "auth status", or "auth logout".');
@@ -3220,11 +3206,33 @@ const parseNonNegativeIntegerFlag = (flags, name) => {
3220
3206
  };
3221
3207
  const run = async () => {
3222
3208
  const parsed = parseArgs(argv.slice(2));
3209
+ if (hasFlag(parsed.flags, 'version')) {
3210
+ stdout.write(`${await cliVersion()}\n`);
3211
+ return;
3212
+ }
3223
3213
  if (hasFlag(parsed.flags, 'help') || parsed.positionals.length === 0) {
3224
3214
  stdout.write(usage);
3225
3215
  return;
3226
3216
  }
3227
3217
  const [command, subcommand] = parsed.positionals;
3218
+ if (command === 'mcp') {
3219
+ if (subcommand !== 'config')
3220
+ throw new Error('Use mcp config --client cursor|codex');
3221
+ stdout.write(mcpConfig(requireFlag(parsed.flags, 'client'), getFlag(parsed.flags, 'base-url') ?? env.ASSETHUB_API_BASE_URL ?? defaultBaseUrl));
3222
+ return;
3223
+ }
3224
+ if (command === 'doctor') {
3225
+ stderr.write('Checking AssetHub connection…\n');
3226
+ const report = await diagnose({
3227
+ resolveAuth: () => resolveAuth(parsed.flags),
3228
+ includeMcp: hasFlag(parsed.flags, 'mcp'),
3229
+ timeoutMs: parsePositiveIntegerFlag(parsed.flags, 'timeout-ms', 15000),
3230
+ });
3231
+ print(report);
3232
+ if (!report.ok)
3233
+ process.exitCode = 2;
3234
+ return;
3235
+ }
3228
3236
  if (command === 'project') {
3229
3237
  if (subcommand !== 'ingest')
3230
3238
  throw new Error('Use project ingest <source-dir> --out-dir <directory>');
@@ -0,0 +1,47 @@
1
+ export declare const cliVersion: () => Promise<string>;
2
+ export declare const mcpConfig: (client: string, baseUrl: string) => string;
3
+ type Check = {
4
+ name: 'api' | 'mcp' | 'configuration';
5
+ status: 'pass' | 'fail';
6
+ code?: string;
7
+ message?: string;
8
+ hint?: string;
9
+ workspaceId?: string;
10
+ executionStatus?: string;
11
+ toolCount?: number;
12
+ };
13
+ type Auth = {
14
+ apiKey: string;
15
+ baseUrl: string;
16
+ profile: string;
17
+ source: string;
18
+ };
19
+ export declare const diagnose: ({ resolveAuth, includeMcp, timeoutMs, }: {
20
+ resolveAuth: () => Promise<Auth>;
21
+ includeMcp: boolean;
22
+ timeoutMs: number;
23
+ }) => Promise<{
24
+ ok: boolean;
25
+ version: string;
26
+ nodeVersion: string;
27
+ checks: {
28
+ name: "configuration";
29
+ status: "fail";
30
+ code: string;
31
+ message: string;
32
+ hint: string;
33
+ }[];
34
+ auth?: undefined;
35
+ } | {
36
+ ok: boolean;
37
+ version: string;
38
+ nodeVersion: string;
39
+ auth: {
40
+ profile: string;
41
+ source: string;
42
+ baseUrl: string;
43
+ };
44
+ checks: [Check, ...Check[]];
45
+ }>;
46
+ export {};
47
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,MAAM,CAKvC,CAAA;AAmBX,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAG,MAoB3D,CAAA;AAED,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,eAAe,CAAA;IACrC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,KAAK,IAAI,GAAG;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAA;AA0D9E,eAAO,MAAM,QAAQ,GAAU,yCAI5B;IACD,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAChC,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB;;;;;;;;;;;;;;;;;;;;;;EAyGA,CAAA"}
package/dist/setup.js ADDED
@@ -0,0 +1,177 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { AssetHubApiError, createAssetHubClient } from '@assethub/api-client';
3
+ export const cliVersion = async () => JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')).version;
4
+ const validatedBaseUrl = (baseUrl) => {
5
+ const url = new URL(baseUrl);
6
+ if (url.username || url.password || url.search || url.hash)
7
+ throw new Error('Use an API URL without credentials, query, or fragment.');
8
+ if (url.protocol !== 'https:' &&
9
+ !(url.protocol === 'http:' &&
10
+ ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)))
11
+ throw new Error('Use HTTPS for remote servers; HTTP is supported only on localhost.');
12
+ return url.href.replace(/\/+$/, '');
13
+ };
14
+ export const mcpConfig = (client, baseUrl) => {
15
+ const url = `${validatedBaseUrl(baseUrl)}/api/mcp`;
16
+ if (client === 'cursor')
17
+ return (JSON.stringify({
18
+ mcpServers: {
19
+ assethub: {
20
+ url,
21
+ headers: { Authorization: 'Bearer ${env:ASSETHUB_API_KEY}' },
22
+ },
23
+ },
24
+ }, null, 2) + '\n');
25
+ if (client === 'codex')
26
+ return `[mcp_servers.assethub]\nurl = ${JSON.stringify(url)}\nbearer_token_env_var = "ASSETHUB_API_KEY"\n`;
27
+ throw new Error('Use mcp config --client cursor|codex');
28
+ };
29
+ const failedCheck = (name, status, timedOut) => {
30
+ if (timedOut)
31
+ return {
32
+ name,
33
+ status: 'fail',
34
+ code: 'TIMEOUT',
35
+ message: 'Connection timed out.',
36
+ hint: 'Check your network or retry with --timeout-ms 30000.',
37
+ };
38
+ if (status === 401)
39
+ return {
40
+ name,
41
+ status: 'fail',
42
+ code: 'UNAUTHORIZED',
43
+ message: 'The API key was rejected.',
44
+ hint: 'Run assethub auth login --api-key-stdin with a current workspace key.',
45
+ };
46
+ if (status === 403)
47
+ return {
48
+ name,
49
+ status: 'fail',
50
+ code: 'FORBIDDEN',
51
+ message: 'This account or workspace does not have access.',
52
+ hint: 'Check the API key workspace and account access.',
53
+ };
54
+ if (name === 'mcp' && status === 404)
55
+ return {
56
+ name,
57
+ status: 'fail',
58
+ code: 'MCP_UNAVAILABLE',
59
+ message: 'Hosted MCP is unavailable for this account or API origin.',
60
+ hint: 'Hosted MCP is in Internal preview. Check access for the account that created the API key.',
61
+ };
62
+ if (status === 429)
63
+ return {
64
+ name,
65
+ status: 'fail',
66
+ code: 'RATE_LIMITED',
67
+ message: 'Too many requests.',
68
+ hint: 'Wait before retrying the check.',
69
+ };
70
+ // Server response text may contain credentials or private data. Report only
71
+ // fixed diagnostics; do not include arbitrary error messages in shareable output.
72
+ return {
73
+ name,
74
+ status: 'fail',
75
+ code: status && status >= 500 ? 'SERVER_ERROR' : 'CONNECTION_FAILED',
76
+ message: 'The connection check failed.',
77
+ hint: 'Check the API origin, network, and service status; then retry.',
78
+ };
79
+ };
80
+ export const diagnose = async ({ resolveAuth, includeMcp, timeoutMs, }) => {
81
+ const version = await cliVersion();
82
+ let auth;
83
+ try {
84
+ auth = await resolveAuth();
85
+ auth.baseUrl = validatedBaseUrl(auth.baseUrl);
86
+ }
87
+ catch {
88
+ return {
89
+ ok: false,
90
+ version,
91
+ nodeVersion: process.version,
92
+ checks: [
93
+ {
94
+ name: 'configuration',
95
+ status: 'fail',
96
+ code: 'AUTH_CONFIGURATION',
97
+ message: 'No usable credentials or API origin for the selected profile.',
98
+ hint: 'Check --profile and --base-url. Select a workspace with assethub workspace use <id> after user login, or run assethub auth login --api-key-stdin.',
99
+ },
100
+ ],
101
+ };
102
+ }
103
+ const signal = AbortSignal.timeout(timeoutMs);
104
+ // Bound the entire transport, including initialize and SSE, and do not follow
105
+ // redirects while carrying the workspace key.
106
+ const boundedFetch = (input, init) => fetch(input, {
107
+ ...init,
108
+ redirect: 'error',
109
+ signal: init?.signal ? AbortSignal.any([init.signal, signal]) : signal,
110
+ });
111
+ const apiCheck = async () => {
112
+ try {
113
+ const capabilities = await createAssetHubClient({
114
+ apiKey: auth.apiKey,
115
+ baseUrl: auth.baseUrl,
116
+ fetch: boundedFetch,
117
+ }).v2.getCapabilities();
118
+ if (typeof capabilities.ownerId !== 'string' || !capabilities.ownerId)
119
+ throw new Error('Invalid capabilities');
120
+ return {
121
+ name: 'api',
122
+ status: 'pass',
123
+ workspaceId: capabilities.ownerId,
124
+ executionStatus: capabilities.executionContext?.status,
125
+ };
126
+ }
127
+ catch (error) {
128
+ return failedCheck('api', error instanceof AssetHubApiError ? error.status : undefined, signal.aborted);
129
+ }
130
+ };
131
+ const mcpCheck = async () => {
132
+ const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
133
+ const { StreamableHTTPClientTransport, StreamableHTTPError } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js');
134
+ const client = new Client({ name: 'assethub-cli-doctor', version });
135
+ try {
136
+ await client.connect(new StreamableHTTPClientTransport(new URL(`${auth.baseUrl}/api/mcp`), {
137
+ fetch: boundedFetch,
138
+ requestInit: { headers: { Authorization: `Bearer ${auth.apiKey}` } },
139
+ }), { signal, timeout: timeoutMs });
140
+ let toolCount = 0;
141
+ let cursor;
142
+ const cursors = new Set();
143
+ do {
144
+ const page = await client.listTools(cursor ? { cursor } : {}, {
145
+ signal,
146
+ timeout: timeoutMs,
147
+ });
148
+ toolCount += page.tools.length;
149
+ cursor = page.nextCursor;
150
+ if (cursor && cursors.has(cursor))
151
+ throw new Error('Repeated tool cursor');
152
+ if (cursor)
153
+ cursors.add(cursor);
154
+ } while (cursor);
155
+ if (!toolCount)
156
+ throw new Error('No MCP tools');
157
+ return { name: 'mcp', status: 'pass', toolCount };
158
+ }
159
+ catch (error) {
160
+ return failedCheck('mcp', error instanceof StreamableHTTPError ? error.code : undefined, signal.aborted);
161
+ }
162
+ finally {
163
+ await client.close();
164
+ }
165
+ };
166
+ const checks = await Promise.all([
167
+ apiCheck(),
168
+ ...(includeMcp ? [mcpCheck()] : []),
169
+ ]);
170
+ return {
171
+ ok: checks.every(check => check.status === 'pass'),
172
+ version,
173
+ nodeVersion: process.version,
174
+ auth: { profile: auth.profile, source: auth.source, baseUrl: auth.baseUrl },
175
+ checks,
176
+ };
177
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assethub/cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Command line interface for AssetHub.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@assethub/api-client": "^0.1.4"
33
+ "@assethub/api-client": "^0.1.4",
34
+ "@modelcontextprotocol/sdk": "^1.30.0"
34
35
  },
35
36
  "repository": {
36
37
  "type": "git",