@assethub/cli 0.1.25 → 0.1.27
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 +31 -2
- package/dist/agentSetup.d.ts +69 -0
- package/dist/agentSetup.d.ts.map +1 -0
- package/dist/agentSetup.js +311 -0
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +19 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -29
- package/dist/setup.d.ts +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +1 -1
- package/package.json +3 -2
- package/skills/assethub/SKILL.md +131 -0
package/README.md
CHANGED
|
@@ -67,6 +67,8 @@ authentication. Existing workspace keys and user-token login remain supported.
|
|
|
67
67
|
```sh
|
|
68
68
|
assethub --version
|
|
69
69
|
assethub doctor --mcp --profile my-workspace
|
|
70
|
+
assethub init --dry-run
|
|
71
|
+
assethub init
|
|
70
72
|
assethub mcp config --client cursor
|
|
71
73
|
assethub mcp config --client codex
|
|
72
74
|
```
|
|
@@ -87,6 +89,19 @@ Use the same key as your CLI profile. Personal profiles include the selected
|
|
|
87
89
|
For a different API origin, pass
|
|
88
90
|
`--base-url https://your-host/prefix` (HTTP is accepted only for localhost).
|
|
89
91
|
|
|
92
|
+
`init` sets up every supported coding agent found on this machine — Claude Code,
|
|
93
|
+
Codex, and Cursor — in one step. It writes the hosted MCP entry into each agent's
|
|
94
|
+
own configuration (merging with what is already there), installs the bundled
|
|
95
|
+
`assethub` skill under `~/.agents/skills/assethub`, and links it into each agent's
|
|
96
|
+
skill directory so the agent knows the correct command sequence, cost rules, and
|
|
97
|
+
recovery steps without being told. Pass `--agent <name>` to choose agents
|
|
98
|
+
explicitly, `--project` to write project-level MCP files into the current
|
|
99
|
+
directory instead of your home, and `--dry-run` to see the plan without writing.
|
|
100
|
+
`init` never stores a key: the MCP entries read `ASSETHUB_API_KEY` from the
|
|
101
|
+
agent's environment. Directories that `init` did not create are left untouched.
|
|
102
|
+
After a CLI upgrade, the next command refreshes the installed skill automatically.
|
|
103
|
+
Set `ASSETHUB_CLI_HOME` to redirect all of these writes, for example in tests.
|
|
104
|
+
|
|
90
105
|
Explicit `--profile` uses that profile's key and origin, ahead of environment
|
|
91
106
|
variables, including user tokens and workspace MFA proofs. A missing profile fails.
|
|
92
107
|
Without an explicit profile, a personal profile or a workspace selected with
|
|
@@ -247,8 +262,10 @@ has its own saved operation ID, with the execution linked to the analysis run.
|
|
|
247
262
|
For Internal `V3.6.1` (`V3.6.1 Primary Images First`), `V3.6.3`
|
|
248
263
|
(`V3.6.3 Fast Analysis`, CLI 0.1.16+), `V3.6.4`
|
|
249
264
|
(`V3.6.4 Fast Analysis`, CLI 0.1.17+), `V3.6.5 Primary Images` and
|
|
250
|
-
`Chibi Character (Pluffy) v3.1` (both CLI 0.1.20+),
|
|
251
|
-
|
|
265
|
+
`Chibi Character (Pluffy) v3.1` (both CLI 0.1.20+), `V3.7 Artist Skills`,
|
|
266
|
+
`V3.7.1 Building Modules` (both CLI 0.1.23+/0.1.24+), `V3.0.9 Garment
|
|
267
|
+
Boundaries`, and `Humanoid Assembly (internal)` (both CLI 0.1.26+), analysis
|
|
268
|
+
runs the complete Artifact Graph split. `--all-ready` waits for that one receipt and does not start a
|
|
252
269
|
second production execution. Resume an interrupted graph split with
|
|
253
270
|
`runs resume <operation-id>`; `--task-id`, `--mission-id`,
|
|
254
271
|
`--part-extraction-mode`, and classic `--order-id` continuation are unsupported.
|
|
@@ -522,6 +539,13 @@ Part extraction uses the same names shown in the AssetHub product: `V1.5`,
|
|
|
522
539
|
`V3.6.4` (`V3.6.4 Fast Analysis`), `V3.6.5` (`V3.6.5 Primary Images`),
|
|
523
540
|
and `pluffy` (`Chibi Character (Pluffy) v3.1`). CLI 0.1.20 adds the latter two;
|
|
524
541
|
`3.6.5`, `pluffy v3.1`, and `pluffy 3.1` are also accepted aliases.
|
|
542
|
+
`V3.7 Artist Skills` (`v3.7`; also known as `V3.0.7 Artist Skills`) and
|
|
543
|
+
`V3.7.1 Building Modules` (`v3.7.1`; also known as `V3.0.8 Building Modules`)
|
|
544
|
+
support `--skill-mode`. `V3.0.9 Garment Boundaries` (`v3.0.9`) and
|
|
545
|
+
`Humanoid Assembly (internal)` (`humanoid assembly`, `humanoid-assembly`,
|
|
546
|
+
`humanoid_assembly_auto`; CLI 0.1.26+) are additional Internal-only graph
|
|
547
|
+
extractors; use the exact quoted public name or one of its aliases with
|
|
548
|
+
`--part-extractor`.
|
|
525
549
|
Run `production agents` to check availability and `supportedOnGraphEndpoint`
|
|
526
550
|
before selecting a graph version. These aliases do not change the V1.5 default
|
|
527
551
|
or grant access to Internal models; the server still authorizes the account.
|
|
@@ -545,6 +569,11 @@ assethub canvas list
|
|
|
545
569
|
assethub canvas use <canvas-id>
|
|
546
570
|
assethub composer models
|
|
547
571
|
assethub composer run --part <mesh-id> --part <mesh-id> --reference <image-id> --wait
|
|
572
|
+
# Composer V6 (auto-assemble) requires each part's reference image; attach it
|
|
573
|
+
# with `<mesh-id>:<part-image-asset-id>` (the image that mesh was generated
|
|
574
|
+
# from) when the mesh has no other lineage the run can resolve it from.
|
|
575
|
+
assethub composer run --part <mesh-id>:<part-image-id> --part <mesh-id>:<part-image-id> \
|
|
576
|
+
--reference <image-id> --model part_composer_v6_auto_assemble --wait
|
|
548
577
|
assethub composer run --from-run <run-id> --transforms-json @transforms.json --wait
|
|
549
578
|
assethub composer refine --list-modes
|
|
550
579
|
assethub composer refine --from-run <run-id> --instruction "align the feet" \
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export declare const AGENT_IDS: readonly ["claude-code", "codex", "cursor"];
|
|
2
|
+
export type AgentId = (typeof AGENT_IDS)[number];
|
|
3
|
+
type AgentSpec = {
|
|
4
|
+
id: AgentId;
|
|
5
|
+
name: string;
|
|
6
|
+
/** Presence of this directory under home means the agent is installed. */
|
|
7
|
+
detectDir: string;
|
|
8
|
+
globalMcpPath: string;
|
|
9
|
+
projectMcpPath: string;
|
|
10
|
+
skillDir: string;
|
|
11
|
+
format: 'claude' | 'cursor' | 'codex';
|
|
12
|
+
};
|
|
13
|
+
/** `ASSETHUB_CLI_HOME` keeps tests and sandboxes from writing into the real home directory. */
|
|
14
|
+
export declare const cliHome: () => string;
|
|
15
|
+
export declare const packagedSkillDir: () => string;
|
|
16
|
+
export declare const canonicalSkillDir: (home: string) => string;
|
|
17
|
+
export declare const mcpServerEntry: (format: AgentSpec["format"], baseUrl: string, workspaceId?: string) => Record<string, unknown>;
|
|
18
|
+
type WriteStatus = 'created' | 'updated' | 'unchanged';
|
|
19
|
+
type SkillStatus = 'installed' | 'updated' | 'unchanged' | 'kept-existing';
|
|
20
|
+
export declare const installSkill: ({ home, version, dryRun, }: {
|
|
21
|
+
home: string;
|
|
22
|
+
version: string;
|
|
23
|
+
dryRun: boolean;
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
path: string;
|
|
26
|
+
status: SkillStatus;
|
|
27
|
+
version?: string;
|
|
28
|
+
}>;
|
|
29
|
+
type LinkStatus = 'linked' | 'relinked' | 'unchanged' | 'kept-existing';
|
|
30
|
+
/**
|
|
31
|
+
* Refresh an installed skill after a CLI upgrade. Runs before every command;
|
|
32
|
+
* the steady-state cost is one small file read. Never throws — a read-only or
|
|
33
|
+
* missing home must not fail the user's actual command.
|
|
34
|
+
*/
|
|
35
|
+
export declare const syncInstalledSkill: () => Promise<void>;
|
|
36
|
+
export type InitOptions = {
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
workspaceId?: string;
|
|
39
|
+
/** Agent ids to configure; defaults to every agent detected under home. */
|
|
40
|
+
agents?: string[];
|
|
41
|
+
/** Write project-level MCP config into the working directory instead of the user's home. */
|
|
42
|
+
project?: boolean;
|
|
43
|
+
dryRun?: boolean;
|
|
44
|
+
cwd?: string;
|
|
45
|
+
home?: string;
|
|
46
|
+
};
|
|
47
|
+
export type InitReport = {
|
|
48
|
+
version: string;
|
|
49
|
+
dryRun: boolean;
|
|
50
|
+
scope: 'global' | 'project';
|
|
51
|
+
skill: Awaited<ReturnType<typeof installSkill>>;
|
|
52
|
+
agents: {
|
|
53
|
+
id: AgentId;
|
|
54
|
+
name: string;
|
|
55
|
+
detected: boolean;
|
|
56
|
+
mcp: {
|
|
57
|
+
path: string;
|
|
58
|
+
status: WriteStatus;
|
|
59
|
+
};
|
|
60
|
+
skill: {
|
|
61
|
+
path: string;
|
|
62
|
+
status: LinkStatus;
|
|
63
|
+
};
|
|
64
|
+
}[];
|
|
65
|
+
next: string[];
|
|
66
|
+
};
|
|
67
|
+
export declare const initAgents: (options: InitOptions) => Promise<InitReport>;
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=agentSetup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentSetup.d.ts","sourceRoot":"","sources":["../src/agentSetup.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,SAAS,6CAA8C,CAAA;AACpE,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhD,KAAK,SAAS,GAAG;IACf,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;CACtC,CAAA;AAoCD,+FAA+F;AAC/F,eAAO,MAAM,OAAO,QAAO,MAA4C,CAAA;AAEvE,eAAO,MAAM,gBAAgB,QAAO,MACiC,CAAA;AAErE,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MACJ,CAAA;AA2B7C,eAAO,MAAM,cAAc,GACzB,QAAQ,SAAS,CAAC,QAAQ,CAAC,EAC3B,SAAS,MAAM,EACf,cAAc,MAAM,KACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAYxB,CAAA;AAED,KAAK,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAA;AA2EtD,KAAK,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,eAAe,CAAA;AAmC1E,eAAO,MAAM,YAAY,GAAU,4BAIhC;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAChB,KAAG,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,CAwBhE,CAAA;AAED,KAAK,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,eAAe,CAAA;AAkCvE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,IAAI,CAevD,CAAA;AAID,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAA;IAC/C,MAAM,EAAE;QACN,EAAE,EAAE,OAAO,CAAA;QACX,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,OAAO,CAAA;QACjB,GAAG,EAAE;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,WAAW,CAAA;SAAC,CAAA;QACxC,KAAK,EAAE;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,UAAU,CAAA;SAAC,CAAA;KAC1C,EAAE,CAAA;IACH,IAAI,EAAE,MAAM,EAAE,CAAA;CACf,CAAA;AAED,eAAO,MAAM,UAAU,GAAU,SAAS,WAAW,KAAG,OAAO,CAAC,UAAU,CA4EzE,CAAA"}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `assethub init` and the skill sync that keeps its output current.
|
|
3
|
+
*
|
|
4
|
+
* Coding agents learn a tool from two local files: an MCP server entry in their
|
|
5
|
+
* own config, and a skill directory they read on demand. `init` writes both for
|
|
6
|
+
* every supported agent found on this machine, merging into existing config
|
|
7
|
+
* rather than replacing it, and never touching credentials — the MCP entries
|
|
8
|
+
* reference `ASSETHUB_API_KEY` from the agent's environment.
|
|
9
|
+
*
|
|
10
|
+
* The skill is copied once to `~/.agents/skills/assethub` and symlinked into
|
|
11
|
+
* each agent's skill directory. A version marker lets every later command
|
|
12
|
+
* refresh the copy when the CLI has been upgraded, so agents never read rules
|
|
13
|
+
* written for a version they are no longer running.
|
|
14
|
+
*/
|
|
15
|
+
import { cp, lstat, mkdir, readFile, readlink, rename, rm, stat, symlink, writeFile, } from 'node:fs/promises';
|
|
16
|
+
import { homedir } from 'node:os';
|
|
17
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
18
|
+
import { cwd as processCwd, env, pid } from 'node:process';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { cliVersion, mcpConfig, validatedBaseUrl } from './setup.js';
|
|
21
|
+
export const AGENT_IDS = ['claude-code', 'codex', 'cursor'];
|
|
22
|
+
const AGENTS = [
|
|
23
|
+
{
|
|
24
|
+
id: 'claude-code',
|
|
25
|
+
name: 'Claude Code',
|
|
26
|
+
detectDir: '.claude',
|
|
27
|
+
globalMcpPath: '.claude.json',
|
|
28
|
+
projectMcpPath: '.mcp.json',
|
|
29
|
+
skillDir: '.claude/skills',
|
|
30
|
+
format: 'claude',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'codex',
|
|
34
|
+
name: 'Codex',
|
|
35
|
+
detectDir: '.codex',
|
|
36
|
+
globalMcpPath: '.codex/config.toml',
|
|
37
|
+
projectMcpPath: '.codex/config.toml',
|
|
38
|
+
skillDir: '.codex/skills',
|
|
39
|
+
format: 'codex',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'cursor',
|
|
43
|
+
name: 'Cursor',
|
|
44
|
+
detectDir: '.cursor',
|
|
45
|
+
globalMcpPath: '.cursor/mcp.json',
|
|
46
|
+
projectMcpPath: '.cursor/mcp.json',
|
|
47
|
+
skillDir: '.cursor/skills',
|
|
48
|
+
format: 'cursor',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
const SERVER_NAME = 'assethub';
|
|
52
|
+
const SKILL_NAME = 'assethub';
|
|
53
|
+
const VERSION_MARKER = '.assethub-cli-version';
|
|
54
|
+
/** `ASSETHUB_CLI_HOME` keeps tests and sandboxes from writing into the real home directory. */
|
|
55
|
+
export const cliHome = () => env.ASSETHUB_CLI_HOME || homedir();
|
|
56
|
+
export const packagedSkillDir = () => fileURLToPath(new URL(`../skills/${SKILL_NAME}/`, import.meta.url));
|
|
57
|
+
export const canonicalSkillDir = (home) => join(home, '.agents', 'skills', SKILL_NAME);
|
|
58
|
+
const isEnoent = (error) => typeof error === 'object' &&
|
|
59
|
+
error !== null &&
|
|
60
|
+
error.code === 'ENOENT';
|
|
61
|
+
const readText = async (path) => {
|
|
62
|
+
try {
|
|
63
|
+
return await readFile(path, 'utf8');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (isEnoent(error))
|
|
67
|
+
return undefined;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const isDirectory = async (path) => {
|
|
72
|
+
try {
|
|
73
|
+
return (await stat(path)).isDirectory();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (isEnoent(error))
|
|
77
|
+
return false;
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
// ── MCP configuration ────────────────────────────────────────────────
|
|
82
|
+
export const mcpServerEntry = (format, baseUrl, workspaceId) => {
|
|
83
|
+
const url = `${validatedBaseUrl(baseUrl)}/api/mcp`;
|
|
84
|
+
// Cursor expands `${env:NAME}`; Claude Code expands `${NAME}`. Neither file ever holds the key.
|
|
85
|
+
const key = format === 'cursor' ? '${env:ASSETHUB_API_KEY}' : '${ASSETHUB_API_KEY}';
|
|
86
|
+
return {
|
|
87
|
+
...(format === 'claude' ? { type: 'http' } : {}),
|
|
88
|
+
url,
|
|
89
|
+
headers: {
|
|
90
|
+
Authorization: `Bearer ${key}`,
|
|
91
|
+
...(workspaceId ? { 'X-AssetHub-Workspace': workspaceId } : {}),
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const writeJsonServer = async (path, entry, dryRun) => {
|
|
96
|
+
const text = await readText(path);
|
|
97
|
+
let document = {};
|
|
98
|
+
if (text !== undefined) {
|
|
99
|
+
let parsed;
|
|
100
|
+
try {
|
|
101
|
+
parsed = JSON.parse(text);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
throw new Error(`${path} is not valid JSON. Fix or move it, then rerun init.`);
|
|
105
|
+
}
|
|
106
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
107
|
+
throw new Error(`${path} must contain a JSON object.`);
|
|
108
|
+
document = parsed;
|
|
109
|
+
}
|
|
110
|
+
// A malformed `mcpServers` is the user's data too; refuse rather than replace it.
|
|
111
|
+
if (document.mcpServers !== undefined &&
|
|
112
|
+
(typeof document.mcpServers !== 'object' ||
|
|
113
|
+
document.mcpServers === null ||
|
|
114
|
+
Array.isArray(document.mcpServers)))
|
|
115
|
+
throw new Error(`${path} has a non-object "mcpServers" value. Fix or move it, then rerun init.`);
|
|
116
|
+
const servers = (document.mcpServers ?? {});
|
|
117
|
+
if (JSON.stringify(servers[SERVER_NAME]) === JSON.stringify(entry))
|
|
118
|
+
return 'unchanged';
|
|
119
|
+
const next = { ...document, mcpServers: { ...servers, [SERVER_NAME]: entry } };
|
|
120
|
+
if (!dryRun) {
|
|
121
|
+
await mkdir(dirname(path), { recursive: true });
|
|
122
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`);
|
|
123
|
+
}
|
|
124
|
+
return text === undefined ? 'created' : 'updated';
|
|
125
|
+
};
|
|
126
|
+
/** Replaces or appends one `[mcp_servers.assethub]` table; every other line is kept verbatim. */
|
|
127
|
+
const writeTomlSection = async (path, section, dryRun) => {
|
|
128
|
+
const header = `[mcp_servers.${SERVER_NAME}]`;
|
|
129
|
+
const body = section.trimEnd();
|
|
130
|
+
const text = await readText(path);
|
|
131
|
+
const lines = (text ?? '').split('\n');
|
|
132
|
+
const start = lines.findIndex(line => line.trim() === header);
|
|
133
|
+
let next;
|
|
134
|
+
if (start === -1) {
|
|
135
|
+
const base = (text ?? '').trimEnd();
|
|
136
|
+
next = `${base ? `${base}\n\n` : ''}${body}\n`;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const endOffset = lines
|
|
140
|
+
.slice(start + 1)
|
|
141
|
+
.findIndex(line => /^\s*\[/.test(line));
|
|
142
|
+
const end = endOffset === -1 ? lines.length : start + 1 + endOffset;
|
|
143
|
+
if (lines.slice(start, end).join('\n').trimEnd() === body)
|
|
144
|
+
return 'unchanged';
|
|
145
|
+
const before = lines.slice(0, start).join('\n').trimEnd();
|
|
146
|
+
const after = lines.slice(end).join('\n').trim();
|
|
147
|
+
next = `${before ? `${before}\n\n` : ''}${body}\n${after ? `\n${after}\n` : ''}`;
|
|
148
|
+
}
|
|
149
|
+
if (!dryRun) {
|
|
150
|
+
await mkdir(dirname(path), { recursive: true });
|
|
151
|
+
await writeFile(path, next);
|
|
152
|
+
}
|
|
153
|
+
return text === undefined ? 'created' : 'updated';
|
|
154
|
+
};
|
|
155
|
+
const readMarker = async (dir) => (await readText(join(dir, VERSION_MARKER)))?.trim();
|
|
156
|
+
/**
|
|
157
|
+
* Stage the new copy beside the target, then swap with two renames, so the
|
|
158
|
+
* installed skill is never deleted before its replacement is complete. A
|
|
159
|
+
* failed swap puts the previous copy back. Two CLIs syncing at the same
|
|
160
|
+
* moment use distinct staging names; whichever copy lands is complete, and
|
|
161
|
+
* the loser's error is swallowed by the caller.
|
|
162
|
+
*/
|
|
163
|
+
const replaceSkillDir = async (source, target, version) => {
|
|
164
|
+
const staging = `${target}.next-${pid}`;
|
|
165
|
+
const previous = `${target}.prev-${pid}`;
|
|
166
|
+
await rm(staging, { recursive: true, force: true });
|
|
167
|
+
await mkdir(dirname(target), { recursive: true });
|
|
168
|
+
await cp(source, staging, { recursive: true });
|
|
169
|
+
await writeFile(join(staging, VERSION_MARKER), `${version}\n`);
|
|
170
|
+
const hadTarget = await isDirectory(target);
|
|
171
|
+
if (hadTarget)
|
|
172
|
+
await rename(target, previous);
|
|
173
|
+
try {
|
|
174
|
+
await rename(staging, target);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (hadTarget)
|
|
178
|
+
await rename(previous, target).catch(() => undefined);
|
|
179
|
+
await rm(staging, { recursive: true, force: true });
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
if (hadTarget)
|
|
183
|
+
await rm(previous, { recursive: true, force: true });
|
|
184
|
+
};
|
|
185
|
+
export const installSkill = async ({ home, version, dryRun, }) => {
|
|
186
|
+
const source = packagedSkillDir();
|
|
187
|
+
if (!(await isDirectory(source)))
|
|
188
|
+
throw new Error('This CLI installation does not include the packaged agent skill.');
|
|
189
|
+
const target = canonicalSkillDir(home);
|
|
190
|
+
const marker = await readMarker(target);
|
|
191
|
+
// A directory without our marker is the user's own; leave it alone.
|
|
192
|
+
const status = marker === undefined
|
|
193
|
+
? (await isDirectory(target))
|
|
194
|
+
? 'kept-existing'
|
|
195
|
+
: 'installed'
|
|
196
|
+
: marker === version
|
|
197
|
+
? 'unchanged'
|
|
198
|
+
: 'updated';
|
|
199
|
+
if (!dryRun && (status === 'installed' || status === 'updated'))
|
|
200
|
+
await replaceSkillDir(source, target, version);
|
|
201
|
+
return {
|
|
202
|
+
path: target,
|
|
203
|
+
status,
|
|
204
|
+
...(status === 'kept-existing' ? {} : { version }),
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
const linkSkill = async (agentSkillsDir, canonical, dryRun) => {
|
|
208
|
+
const link = join(agentSkillsDir, SKILL_NAME);
|
|
209
|
+
const relativeTarget = relative(agentSkillsDir, canonical);
|
|
210
|
+
let existing;
|
|
211
|
+
try {
|
|
212
|
+
existing = await lstat(link);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (!isEnoent(error))
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
if (existing?.isSymbolicLink()) {
|
|
219
|
+
if (resolve(agentSkillsDir, await readlink(link)) === canonical)
|
|
220
|
+
return { path: link, status: 'unchanged' };
|
|
221
|
+
if (!dryRun) {
|
|
222
|
+
await rm(link);
|
|
223
|
+
await symlink(relativeTarget, link, 'dir');
|
|
224
|
+
}
|
|
225
|
+
return { path: link, status: 'relinked' };
|
|
226
|
+
}
|
|
227
|
+
if (existing)
|
|
228
|
+
return { path: link, status: 'kept-existing' };
|
|
229
|
+
if (!dryRun) {
|
|
230
|
+
await mkdir(agentSkillsDir, { recursive: true });
|
|
231
|
+
await symlink(relativeTarget, link, 'dir');
|
|
232
|
+
}
|
|
233
|
+
return { path: link, status: 'linked' };
|
|
234
|
+
};
|
|
235
|
+
let synced = false;
|
|
236
|
+
/**
|
|
237
|
+
* Refresh an installed skill after a CLI upgrade. Runs before every command;
|
|
238
|
+
* the steady-state cost is one small file read. Never throws — a read-only or
|
|
239
|
+
* missing home must not fail the user's actual command.
|
|
240
|
+
*/
|
|
241
|
+
export const syncInstalledSkill = async () => {
|
|
242
|
+
if (synced)
|
|
243
|
+
return;
|
|
244
|
+
synced = true;
|
|
245
|
+
try {
|
|
246
|
+
const target = canonicalSkillDir(cliHome());
|
|
247
|
+
const installed = await readMarker(target);
|
|
248
|
+
if (installed === undefined)
|
|
249
|
+
return;
|
|
250
|
+
const version = await cliVersion();
|
|
251
|
+
if (installed === version)
|
|
252
|
+
return;
|
|
253
|
+
const source = packagedSkillDir();
|
|
254
|
+
if (!(await isDirectory(source)))
|
|
255
|
+
return;
|
|
256
|
+
await replaceSkillDir(source, target, version);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// best effort
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
export const initAgents = async (options) => {
|
|
263
|
+
const home = options.home ?? cliHome();
|
|
264
|
+
const cwd = options.cwd ?? processCwd();
|
|
265
|
+
const dryRun = options.dryRun ?? false;
|
|
266
|
+
const version = await cliVersion();
|
|
267
|
+
const requested = options.agents ?? [];
|
|
268
|
+
const unknown = requested.filter(id => !AGENT_IDS.includes(id));
|
|
269
|
+
if (unknown.length > 0)
|
|
270
|
+
throw new Error(`Unknown agent: ${unknown.join(', ')}. Use --agent ${AGENT_IDS.join('|')}.`);
|
|
271
|
+
const detected = new Map(await Promise.all(AGENTS.map(async (agent) => [agent.id, await isDirectory(join(home, agent.detectDir))])));
|
|
272
|
+
const selected = requested.length > 0
|
|
273
|
+
? AGENTS.filter(agent => requested.includes(agent.id))
|
|
274
|
+
: AGENTS.filter(agent => detected.get(agent.id));
|
|
275
|
+
if (selected.length === 0)
|
|
276
|
+
throw new Error(`No supported coding agent found under ${home}. Pass --agent ${AGENT_IDS.join('|')} to choose one.`);
|
|
277
|
+
const skill = await installSkill({ home, version, dryRun });
|
|
278
|
+
const agents = [];
|
|
279
|
+
for (const spec of selected) {
|
|
280
|
+
const mcpPath = options.project
|
|
281
|
+
? join(cwd, spec.projectMcpPath)
|
|
282
|
+
: join(home, spec.globalMcpPath);
|
|
283
|
+
const mcp = spec.format === 'codex'
|
|
284
|
+
? await writeTomlSection(mcpPath, mcpConfig('codex', options.baseUrl, false, options.workspaceId), dryRun)
|
|
285
|
+
: await writeJsonServer(mcpPath, mcpServerEntry(spec.format, options.baseUrl, options.workspaceId), dryRun);
|
|
286
|
+
const link = await linkSkill(join(home, spec.skillDir), skill.path, dryRun);
|
|
287
|
+
agents.push({
|
|
288
|
+
id: spec.id,
|
|
289
|
+
name: spec.name,
|
|
290
|
+
detected: detected.get(spec.id) ?? false,
|
|
291
|
+
mcp: { path: mcpPath, status: mcp },
|
|
292
|
+
skill: link,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
version,
|
|
297
|
+
dryRun,
|
|
298
|
+
scope: options.project ? 'project' : 'global',
|
|
299
|
+
skill,
|
|
300
|
+
agents,
|
|
301
|
+
next: [
|
|
302
|
+
...(env.ASSETHUB_API_KEY
|
|
303
|
+
? []
|
|
304
|
+
: [
|
|
305
|
+
"Provide ASSETHUB_API_KEY in each agent's environment; the MCP entries read it from there and never store it.",
|
|
306
|
+
]),
|
|
307
|
+
'Restart the agent so it reloads MCP servers and skills.',
|
|
308
|
+
'Run `assethub doctor --mcp` to verify the connection.',
|
|
309
|
+
],
|
|
310
|
+
};
|
|
311
|
+
};
|
package/dist/execution.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../src/execution.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EACjC,MAAM,sBAAsB,CAAA;AAG7B,QAAA,MAAM,UAAU,kJAQN,CAAA;AACV,KAAK,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAC5C,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AACD,eAAO,MAAM,oBAAoB,GAAU,SAAS,cAAc;;;;;YAPxD,cAAc;cACZ,MAAM;SACX,MAAM;eACA,MAAM;gBACL,SAAS;aACZ,OAAO;EAoBjB,CAAA;AACD,KAAK,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAA;AAC/D,KAAK,WAAW,GACZ,sBAAsB,GACtB,qBAAqB,GACrB,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,GAC5C,IAAI,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,GAC3C,wBAAwB,GACxB,wBAAwB,GACxB,2BAA2B,CAAA;AAU/B,eAAO,MAAM,oBAAoB,GAC/B,UAAU,MAAM,EAChB,aAAa,MAAM,qBACyD,CAAA;AAC9E,KAAK,aAAa,GAAG;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACnC,WAAW,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC7C,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;CAC5C,CAAA;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAGxC,QAAQ,CAAC,QAAQ;IACjB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS;gBAJlC,OAAO,EAAE,MAAM,EACN,QAAQ,SAAI,EACZ,WAAW,CAAC,EAAE,MAAM,YAAA,EACpB,SAAS,CAAC,EAAE,eAAe,YAAA,EAC3B,KAAK,GAAE,MAAM,GAAG,SAA4B;CAIxD;
|
|
1
|
+
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../src/execution.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EACjC,MAAM,sBAAsB,CAAA;AAG7B,QAAA,MAAM,UAAU,kJAQN,CAAA;AACV,KAAK,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAC5C,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AACD,eAAO,MAAM,oBAAoB,GAAU,SAAS,cAAc;;;;;YAPxD,cAAc;cACZ,MAAM;SACX,MAAM;eACA,MAAM;gBACL,SAAS;aACZ,OAAO;EAoBjB,CAAA;AACD,KAAK,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAA;AAC/D,KAAK,WAAW,GACZ,sBAAsB,GACtB,qBAAqB,GACrB,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,GAC5C,IAAI,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,GAC3C,wBAAwB,GACxB,wBAAwB,GACxB,2BAA2B,CAAA;AAU/B,eAAO,MAAM,oBAAoB,GAC/B,UAAU,MAAM,EAChB,aAAa,MAAM,qBACyD,CAAA;AAC9E,KAAK,aAAa,GAAG;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACnC,WAAW,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC7C,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;CAC5C,CAAA;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAGxC,QAAQ,CAAC,QAAQ;IACjB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS;gBAJlC,OAAO,EAAE,MAAM,EACN,QAAQ,SAAI,EACZ,WAAW,CAAC,EAAE,MAAM,YAAA,EACpB,SAAS,CAAC,EAAE,eAAe,YAAA,EAC3B,KAAK,GAAE,MAAM,GAAG,SAA4B;CAIxD;AAMD,yFAAyF;AACzF,eAAO,MAAM,eAAe,EAAE;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,eAAe,CAAA;CACvB,CAAA;AAEN,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,WAK5D,CAAA;AAuKD,eAAO,MAAM,eAAe,GAC1B,SAAS,OAAO,EAChB,WAAW,SAAS,EACpB,MAAM,WAAW,EACjB,UAAS,aAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqE5B,CAAA;AAED,eAAO,MAAM,cAAc,GACzB,SAAS,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,WAAW,CAAC,GAAG;IACxD,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtC,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyDF,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,cAAc,EACtB,OAAO,MAAM,EACb,UAAS;IACP,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAA;CAC7C,KACL,OAAO,CAAC,eAAe,CA0DzB,CAAA"}
|
package/dist/execution.js
CHANGED
|
@@ -43,6 +43,9 @@ export class CliExecutionError extends Error {
|
|
|
43
43
|
this.runId = runId;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
+
/** A floor under `--interval-ms` for `waitForExecution`'s own poll cadence. */
|
|
47
|
+
const MIN_POLL_INTERVAL_MS = 1_000;
|
|
48
|
+
const POLL_JITTER_MS = 250;
|
|
46
49
|
/** Kept only for SIGINT reporting; interrupting the client never cancels server work. */
|
|
47
50
|
export const activeExecution = {};
|
|
48
51
|
export const childOperationId = (parent, step) => {
|
|
@@ -247,6 +250,11 @@ export const waitForExecution = async (client, runId, options = {}) => {
|
|
|
247
250
|
activeExecution.runId = runId;
|
|
248
251
|
const deadline = Date.now() + (options.timeoutMs ?? 900_000);
|
|
249
252
|
const timeout = () => new CliExecutionError(`Wait timed out; server execution continues. Use runs watch ${runId}.`, 3, activeExecution.operationId, activeExecution.execution, runId);
|
|
253
|
+
// A floor under --interval-ms and a touch of jitter on every poll: several
|
|
254
|
+
// `--wait` invocations started together (e.g. parallel `mesh generate`)
|
|
255
|
+
// otherwise poll in lockstep and collide on the same rate-limit window.
|
|
256
|
+
const pollIntervalMs = () => Math.max(options.intervalMs ?? 5_000, MIN_POLL_INTERVAL_MS) +
|
|
257
|
+
Math.floor(Math.random() * POLL_JITTER_MS);
|
|
250
258
|
while (true) {
|
|
251
259
|
const signal = AbortSignal.timeout(Math.max(1, deadline - Date.now()));
|
|
252
260
|
let execution;
|
|
@@ -256,6 +264,16 @@ export const waitForExecution = async (client, runId, options = {}) => {
|
|
|
256
264
|
catch (error) {
|
|
257
265
|
if (signal.aborted)
|
|
258
266
|
throw timeout();
|
|
267
|
+
// The client already retries a 429 internally (Retry-After/backoff);
|
|
268
|
+
// one only reaches here once that budget is exhausted. Server work
|
|
269
|
+
// keeps running regardless, so a rate limit is never a wait failure —
|
|
270
|
+
// treat it like "no update yet" and keep polling within the deadline.
|
|
271
|
+
if (error instanceof AssetHubApiError && error.status === 429) {
|
|
272
|
+
if (Date.now() >= deadline)
|
|
273
|
+
throw timeout();
|
|
274
|
+
await delay(Math.min(pollIntervalMs(), Math.max(0, deadline - Date.now())));
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
259
277
|
throw new CliExecutionError(error instanceof Error ? error.message : String(error), error instanceof AssetHubApiError && error.status < 500 ? 2 : 1, activeExecution.operationId, activeExecution.execution, runId);
|
|
260
278
|
}
|
|
261
279
|
activeExecution.execution = execution;
|
|
@@ -267,6 +285,6 @@ export const waitForExecution = async (client, runId, options = {}) => {
|
|
|
267
285
|
return execution;
|
|
268
286
|
if (Date.now() >= deadline)
|
|
269
287
|
throw timeout();
|
|
270
|
-
await delay(Math.min(
|
|
288
|
+
await delay(Math.min(pollIntervalMs(), Math.max(0, deadline - Date.now())));
|
|
271
289
|
}
|
|
272
290
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ type CommandContext = {
|
|
|
22
22
|
*/
|
|
23
23
|
args: string[];
|
|
24
24
|
};
|
|
25
|
-
export declare const usage = "AssetHub CLI\n\nCLI and MCP are available to all AssetHub users. Workspace permissions and feature availability apply.\n\nUsage:\n assethub --version\n assethub doctor [--mcp] [--profile <name>] [--timeout-ms <n>]\n assethub mcp tools [tool-name] [--account] [--profile <name>] [--timeout-ms <n>]\n assethub mcp config --client cursor|codex [--account] [--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 [--query <text>] [--limit <1-100>] [--cursor <cursor>]\n assethub workspace get\n assethub workspace create --name <name> [--operation-id <uuid>]\n assethub workspace use <workspace-id>\n assethub workspace members [--workspace <id>]\n assethub workspace invite --email <email> [--role user|admin] [--resend] [--workspace <id>]\n assethub workspace set-role <user-id> --role user|admin [--workspace <id>]\n assethub workspace remove-member <user-id> [--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 download --canvas <id> --out-dir <directory> [--mesh <mesh_asset_id>]\n assethub canvas nodes --canvas <id>\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 api search [query]\n assethub api describe \"<METHOD /path>\"\n assethub api call \"<METHOD /path>\" [--path-json <json|@file>] [--query-json <json|@file>] [--input-json <json|@file|@->] [--operation-id <uuid>]\n assethub skills list [--cursor <cursor>]\n assethub skills get <skill-id> [--revision <n>]\n assethub skills learn --input-json <json|@file|@-> --operation-id <uuid>\n assethub skills update <skill-id> --input-json <json|@file|@->\n assethub skills controls <skill-id> --revision <n> --grant-revision <n> --mode automatic|manual|off [--share]\n assethub skills prepare --input-json <json|@file|@->\n assethub skills proposal <proposal-id>\n assethub skills accept <proposal-id> --input-json <json|@file|@->\n assethub composer models\n assethub composer refine --list-modes\n assethub composer refine --from-run <run-id> --instruction <text> [--mode standard|thorough|placement|workshop|blender|codex] [--max-rounds <n>] [--transforms-json <json|@file>] [--wait] [--download --out-dir <dir>]\n assethub composer refine --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]\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 composer run --node <shape:id> --canvas <id> [--model <id>] [--mode quick|quality] [--operation-id <uuid>] [--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 list [--cursor <cursor>] [--limit <n>]\n assethub graph show|export [--canvas <id> | --graph <id>] [--source generated|upload] [--artifact <node-id>] [--out <path>] [--allow-truncated]\n assethub graph lineage --artifact <node-id> [--canvas <id> | --graph <id>] [--source generated|upload] [--direction ancestors|descendants|both] [--depth <n>] [--out <path>]\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 mesh generate --node <shape:id> --canvas <id> [--model-id <id>] [--params-json <json|@file>] [--operation-id <uuid>] [--wait]\n assethub mesh list [--query <text>] [--cursor <cursor>] [--limit <n>]\n assethub mesh get <mesh-asset-id>\n assethub mesh download <mesh-asset-id> --out-dir <directory>\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>] [--skill-mode auto|manual|off] [--skill-id <id>...] [--context <canvas-id> --context-version <n>] [--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>] [--skill-mode auto|manual|off] [--skill-id <id>...] [--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 --workspace <id> Select workspace scope for this command without changing saved selection.\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";
|
|
25
|
+
export declare const usage = "AssetHub CLI\n\nCLI and MCP are available to all AssetHub users. Workspace permissions and feature availability apply.\n\nUsage:\n assethub --version\n assethub doctor [--mcp] [--profile <name>] [--timeout-ms <n>]\n assethub init [--agent claude-code|codex|cursor...] [--project] [--dry-run] [--base-url <url>] [--workspace <id>] [--profile <name>]\n assethub mcp tools [tool-name] [--account] [--profile <name>] [--timeout-ms <n>]\n assethub mcp config --client cursor|codex [--account] [--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 [--query <text>] [--limit <1-100>] [--cursor <cursor>]\n assethub workspace get\n assethub workspace create --name <name> [--operation-id <uuid>]\n assethub workspace use <workspace-id>\n assethub workspace members [--workspace <id>]\n assethub workspace invite --email <email> [--role user|admin] [--resend] [--workspace <id>]\n assethub workspace set-role <user-id> --role user|admin [--workspace <id>]\n assethub workspace remove-member <user-id> [--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 download --canvas <id> --out-dir <directory> [--mesh <mesh_asset_id>]\n assethub canvas nodes --canvas <id>\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 api search [query]\n assethub api describe \"<METHOD /path>\"\n assethub api call \"<METHOD /path>\" [--path-json <json|@file>] [--query-json <json|@file>] [--input-json <json|@file|@->] [--operation-id <uuid>]\n assethub skills list [--cursor <cursor>]\n assethub skills get <skill-id> [--revision <n>]\n assethub skills learn --input-json <json|@file|@-> --operation-id <uuid>\n assethub skills update <skill-id> --input-json <json|@file|@->\n assethub skills controls <skill-id> --revision <n> --grant-revision <n> --mode automatic|manual|off [--share]\n assethub skills prepare --input-json <json|@file|@->\n assethub skills proposal <proposal-id>\n assethub skills accept <proposal-id> --input-json <json|@file|@->\n assethub composer models\n assethub composer refine --list-modes\n assethub composer refine --from-run <run-id> --instruction <text> [--mode standard|thorough|placement|workshop|blender|codex] [--max-rounds <n>] [--transforms-json <json|@file>] [--wait] [--download --out-dir <dir>]\n assethub composer refine --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]\n assethub composer run --part <mesh-asset-id>[:<part-image-asset-id>] [--part <mesh-asset-id>[:<part-image-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 composer run --node <shape:id> --canvas <id> [--model <id>] [--mode quick|quality] [--operation-id <uuid>] [--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 list [--cursor <cursor>] [--limit <n>]\n assethub graph show|export [--canvas <id> | --graph <id>] [--source generated|upload] [--artifact <node-id>] [--out <path>] [--allow-truncated]\n assethub graph lineage --artifact <node-id> [--canvas <id> | --graph <id>] [--source generated|upload] [--direction ancestors|descendants|both] [--depth <n>] [--out <path>]\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 mesh generate --node <shape:id> --canvas <id> [--model-id <id>] [--params-json <json|@file>] [--operation-id <uuid>] [--wait]\n assethub mesh list [--query <text>] [--cursor <cursor>] [--limit <n>]\n assethub mesh get <mesh-asset-id>\n assethub mesh download <mesh-asset-id> --out-dir <directory>\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>] [--skill-mode auto|manual|off] [--skill-id <id>...] [--context <canvas-id> --context-version <n>] [--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>] [--skill-mode auto|manual|off] [--skill-id <id>...] [--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 --workspace <id> Select workspace scope for this command without changing saved selection.\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 init --dry-run\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";
|
|
26
26
|
export declare const commandSkills: (subcommand: string | undefined, positionals: string[], ctx: CommandContext) => Promise<void>;
|
|
27
27
|
/**
|
|
28
28
|
* The `animate retarget` request, built from flags alone.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA6BA,OAAO,EAQL,KAAK,wBAAwB,EAO7B,KAAK,YAAY,EAEjB,KAAK,UAAU,EAef,oBAAoB,EACrB,MAAM,sBAAsB,CAAA;AAc7B,OAAO,EAAC,kBAAkB,EAAc,MAAM,gCAAgC,CAAA;AAI9E,KAAK,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;AAuBxD,KAAK,YAAY,GAAG;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,UAAU,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAA;CACnC,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;IAC/C,KAAK,EAAE,KAAK,CAAA;IACZ,IAAI,EAAE,YAAY,CAAA;IAClB;;;;;OAKG;IACH,IAAI,EAAE,MAAM,EAAE,CAAA;CACf,CAAA;AA4ID,eAAO,MAAM,KAAK,28cA+IjB,CAAA;AA2gDD,eAAO,MAAM,aAAa,GACxB,YAAY,MAAM,GAAG,SAAS,EAC9B,aAAa,MAAM,EAAE,EACrB,KAAK,cAAc,KAClB,OAAO,CAAC,IAAI,CA6Hd,CAAA;AA61CD;;;;;;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;AA+tCD,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,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
3
|
import { cliVersion, diagnose, mcpConfig } from './setup.js';
|
|
4
|
+
import { AGENT_IDS, initAgents, syncInstalledSkill } from './agentSetup.js';
|
|
4
5
|
import { ingestProjectSources } from './projectSources.js';
|
|
5
6
|
import { downloadCanvas } from './canvasDownload.js';
|
|
6
7
|
import { executeNodeMeshBatch, resumeNodeMeshBatch, } from './nodeMeshBatch.js';
|
|
@@ -30,15 +31,6 @@ const defaultImageModelId = 'imageGen.nanoBanana2.openrouter';
|
|
|
30
31
|
const defaultMeshModelId = 'meshGen.hunyuan31';
|
|
31
32
|
const defaultProfileName = 'default';
|
|
32
33
|
const defaultPartExtractorName = 'V1.5';
|
|
33
|
-
const artifactGraphPartExtractorApiValues = new Set([
|
|
34
|
-
'ah_agent_graph_v3_6_1',
|
|
35
|
-
'ah_agent_graph_v3_6_3',
|
|
36
|
-
'ah_agent_graph_v3_6_4',
|
|
37
|
-
'ah_agent_graph_v3_6_5',
|
|
38
|
-
'ah_agent_graph_pluffy_v3_1',
|
|
39
|
-
'ah_agent_graph_v3_7',
|
|
40
|
-
'ah_agent_graph_v3_7_1',
|
|
41
|
-
]);
|
|
42
34
|
const partExtractorOptions = [
|
|
43
35
|
{
|
|
44
36
|
publicName: 'V1.5',
|
|
@@ -80,17 +72,34 @@ const partExtractorOptions = [
|
|
|
80
72
|
apiValue: 'ah_agent_graph_pluffy_v3_1',
|
|
81
73
|
aliases: ['pluffy', 'pluffy v3.1', 'pluffy 3.1'],
|
|
82
74
|
},
|
|
75
|
+
{
|
|
76
|
+
publicName: 'V3.0.9 Garment Boundaries',
|
|
77
|
+
apiValue: 'ah_agent_graph_v3_7_2',
|
|
78
|
+
aliases: ['v3.0.9', '3.0.9'],
|
|
79
|
+
},
|
|
83
80
|
{
|
|
84
81
|
publicName: 'V3.7 Artist Skills',
|
|
85
82
|
apiValue: 'ah_agent_graph_v3_7',
|
|
86
|
-
|
|
83
|
+
// The web picker and `production agents` call it V3.0.7.
|
|
84
|
+
aliases: ['v3.7', '3.7', 'v3.0.7 artist skills', 'v3.0.7', '3.0.7'],
|
|
87
85
|
},
|
|
88
86
|
{
|
|
89
87
|
publicName: 'V3.7.1 Building Modules',
|
|
90
88
|
apiValue: 'ah_agent_graph_v3_7_1',
|
|
91
|
-
|
|
89
|
+
// The web picker and `production agents` call it V3.0.8.
|
|
90
|
+
aliases: ['v3.7.1', '3.7.1', 'v3.0.8 building modules', 'v3.0.8', '3.0.8'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
publicName: 'Humanoid Assembly (internal)',
|
|
94
|
+
apiValue: 'ah_agent_graph_humanoid_assembly_auto',
|
|
95
|
+
aliases: ['humanoid assembly', 'humanoid-assembly', 'humanoid_assembly_auto'],
|
|
92
96
|
},
|
|
93
97
|
];
|
|
98
|
+
// Graph-engine extractors run through the artifact-graph endpoint. Derived from
|
|
99
|
+
// the option list so a newly listed graph agent cannot miss the graph path.
|
|
100
|
+
const artifactGraphPartExtractorApiValues = new Set(partExtractorOptions
|
|
101
|
+
.map(option => option.apiValue)
|
|
102
|
+
.filter(apiValue => apiValue.startsWith('ah_agent_graph_')));
|
|
94
103
|
const partExtractorPublicNames = partExtractorOptions
|
|
95
104
|
.map(option => option.publicName)
|
|
96
105
|
.join(', ');
|
|
@@ -100,7 +109,12 @@ const partExtractorByInput = new Map(partExtractorOptions.flatMap(option => [
|
|
|
100
109
|
...option.aliases.map(alias => [normalizePartExtractorName(alias), option]),
|
|
101
110
|
]));
|
|
102
111
|
const partExtractorPublicNameByApiValue = new Map(partExtractorOptions.map(option => [option.apiValue, option.publicName]));
|
|
103
|
-
|
|
112
|
+
// Every registered internal apiValue is an `ah_...` id (see partExtractorOptions
|
|
113
|
+
// above). Matching that prefix instead of a blanket "contains an underscore"
|
|
114
|
+
// check keeps this from rejecting a public alias that happens to use
|
|
115
|
+
// underscores (e.g. 'humanoid_assembly_auto', mirroring the engine profile
|
|
116
|
+
// name) while still catching every real internal id.
|
|
117
|
+
const isInternalPartExtractorId = (value) => value.trim().toLowerCase().startsWith('ah_');
|
|
104
118
|
const execFileAsync = promisify(execFile);
|
|
105
119
|
export const usage = `AssetHub CLI
|
|
106
120
|
|
|
@@ -109,6 +123,7 @@ CLI and MCP are available to all AssetHub users. Workspace permissions and featu
|
|
|
109
123
|
Usage:
|
|
110
124
|
assethub --version
|
|
111
125
|
assethub doctor [--mcp] [--profile <name>] [--timeout-ms <n>]
|
|
126
|
+
assethub init [--agent claude-code|codex|cursor...] [--project] [--dry-run] [--base-url <url>] [--workspace <id>] [--profile <name>]
|
|
112
127
|
assethub mcp tools [tool-name] [--account] [--profile <name>] [--timeout-ms <n>]
|
|
113
128
|
assethub mcp config --client cursor|codex [--account] [--base-url <url>]
|
|
114
129
|
assethub auth login --api-key <key> [--base-url <url>] [--profile <name>]
|
|
@@ -162,7 +177,7 @@ Usage:
|
|
|
162
177
|
assethub composer refine --list-modes
|
|
163
178
|
assethub composer refine --from-run <run-id> --instruction <text> [--mode standard|thorough|placement|workshop|blender|codex] [--max-rounds <n>] [--transforms-json <json|@file>] [--wait] [--download --out-dir <dir>]
|
|
164
179
|
assethub composer refine --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]
|
|
165
|
-
assethub composer run --part <mesh-asset-id> [--part <mesh-asset-id
|
|
180
|
+
assethub composer run --part <mesh-asset-id>[:<part-image-asset-id>] [--part <mesh-asset-id>[:<part-image-asset-id>]...] --reference <image-asset-id> [--model <id>] [--mode quick|quality] [--transforms-json <json|@file>] [--canvas <id>] [--wait]
|
|
166
181
|
assethub composer run --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]
|
|
167
182
|
assethub composer run --from-run <run-id> [--transforms-json <json|@file>] [--mode quick|quality] [--wait]
|
|
168
183
|
assethub composer run --node <shape:id> --canvas <id> [--model <id>] [--mode quick|quality] [--operation-id <uuid>] [--wait]
|
|
@@ -221,6 +236,7 @@ Global options:
|
|
|
221
236
|
Examples:
|
|
222
237
|
ASSETHUB_API_KEY=ah_live_xxx assethub models list
|
|
223
238
|
assethub auth login --api-key-stdin
|
|
239
|
+
assethub init --dry-run
|
|
224
240
|
assethub models list --ids-only
|
|
225
241
|
assethub files upload ./input.png --media-type image
|
|
226
242
|
assethub source create --file ./input.png --media-type image
|
|
@@ -728,6 +744,25 @@ const assertFlagHasValue = (value, flagDescription) => {
|
|
|
728
744
|
}
|
|
729
745
|
return value;
|
|
730
746
|
};
|
|
747
|
+
/**
|
|
748
|
+
* Parses one `--part` value for `composer run`/`composer refine` into a
|
|
749
|
+
* composer part. Plain `<mesh-asset-id>` composes without a part-reference
|
|
750
|
+
* image (Composer V6 then requires it via `--input-json` or falls back to
|
|
751
|
+
* the mesh's own thumbnail, if enabled). `<mesh-asset-id>:<part-image-asset-id>`
|
|
752
|
+
* attaches the image the part's mesh was generated from, matching what the
|
|
753
|
+
* canvas node graph supplies automatically for UI-driven runs.
|
|
754
|
+
*/
|
|
755
|
+
const parsePartFlagValue = (raw) => {
|
|
756
|
+
const value = assertFlagHasValue(raw, '--part <mesh-asset-id>[:<part-image-asset-id>]');
|
|
757
|
+
const separatorIndex = value.indexOf(':');
|
|
758
|
+
if (separatorIndex === -1)
|
|
759
|
+
return { assetId: value };
|
|
760
|
+
const assetId = value.slice(0, separatorIndex);
|
|
761
|
+
const partImageAssetId = value.slice(separatorIndex + 1);
|
|
762
|
+
if (!assetId || !partImageAssetId)
|
|
763
|
+
throw new Error('--part must be <mesh-asset-id> or <mesh-asset-id>:<part-image-asset-id>');
|
|
764
|
+
return { assetId, partImageAssetId };
|
|
765
|
+
};
|
|
731
766
|
const sourceInputCount = (flags) => [
|
|
732
767
|
getSourceResourceIdFlag(flags) != null,
|
|
733
768
|
getSourceUrlFlag(flags) != null,
|
|
@@ -2243,9 +2278,7 @@ const commandComposerRefine = async (ctx) => {
|
|
|
2243
2278
|
: previous
|
|
2244
2279
|
? composerInputFromRun(previous, 'mesh.refine')
|
|
2245
2280
|
: {
|
|
2246
|
-
parts: parts.map(
|
|
2247
|
-
assetId: assertFlagHasValue(assetId, '--part <asset-id>'),
|
|
2248
|
-
})),
|
|
2281
|
+
parts: parts.map(parsePartFlagValue),
|
|
2249
2282
|
fullBodyImageAssetId: requireFlag(ctx.flags, 'reference'),
|
|
2250
2283
|
};
|
|
2251
2284
|
delete input.executionContext;
|
|
@@ -2354,9 +2387,7 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
2354
2387
|
: json
|
|
2355
2388
|
? await readJsonArgument(json, '--input-json')
|
|
2356
2389
|
: {
|
|
2357
|
-
parts: parts.map(
|
|
2358
|
-
assetId: assertFlagHasValue(assetId, '--part <asset-id>'),
|
|
2359
|
-
})),
|
|
2390
|
+
parts: parts.map(parsePartFlagValue),
|
|
2360
2391
|
fullBodyImageAssetId: requireFlag(ctx.flags, 'reference'),
|
|
2361
2392
|
};
|
|
2362
2393
|
delete input.executionContext;
|
|
@@ -4003,6 +4034,20 @@ const parseNonNegativeIntegerFlag = (flags, name) => {
|
|
|
4003
4034
|
}
|
|
4004
4035
|
return parsed;
|
|
4005
4036
|
};
|
|
4037
|
+
/** Base URL and workspace an MCP entry should target: explicit flags, then the selected profile, then defaults. */
|
|
4038
|
+
const resolveMcpTarget = async (flags) => {
|
|
4039
|
+
const config = await readAuthConfig(getConfigPath(flags));
|
|
4040
|
+
const explicitProfile = getFlag(flags, 'profile');
|
|
4041
|
+
const stored = config.profiles?.[explicitProfile ?? config.defaultProfile ?? defaultProfileName];
|
|
4042
|
+
if (explicitProfile && !stored)
|
|
4043
|
+
throw new Error('Selected profile does not exist');
|
|
4044
|
+
const selected = explicitProfile || stored?.workspaceId || isPersonalProfile(stored);
|
|
4045
|
+
const baseUrl = getFlag(flags, 'base-url') ??
|
|
4046
|
+
(selected ? stored?.baseUrl : env.ASSETHUB_API_BASE_URL ?? stored?.baseUrl) ?? defaultBaseUrl;
|
|
4047
|
+
const workspaceId = getFlag(flags, 'workspace') ??
|
|
4048
|
+
(stored && new URL(baseUrl).origin === new URL(stored.baseUrl).origin ? stored.workspaceId : undefined);
|
|
4049
|
+
return { baseUrl, workspaceId };
|
|
4050
|
+
};
|
|
4006
4051
|
const run = async () => {
|
|
4007
4052
|
const parsed = parseArgs(argv.slice(2));
|
|
4008
4053
|
if (hasFlag(parsed.flags, 'version')) {
|
|
@@ -4014,6 +4059,20 @@ const run = async () => {
|
|
|
4014
4059
|
return;
|
|
4015
4060
|
}
|
|
4016
4061
|
const [command, subcommand] = parsed.positionals;
|
|
4062
|
+
if (command !== 'init')
|
|
4063
|
+
await syncInstalledSkill();
|
|
4064
|
+
if (command === 'init') {
|
|
4065
|
+
if (subcommand)
|
|
4066
|
+
throw new Error(`Use init [--agent ${AGENT_IDS.join('|')}] [--project] [--dry-run]`);
|
|
4067
|
+
const target = await resolveMcpTarget(parsed.flags);
|
|
4068
|
+
print(await initAgents({
|
|
4069
|
+
...target,
|
|
4070
|
+
agents: getFlagValues(parsed.flags, 'agent').map(value => assertFlagHasValue(value, '--agent <name>')),
|
|
4071
|
+
project: hasFlag(parsed.flags, 'project'),
|
|
4072
|
+
dryRun: hasFlag(parsed.flags, 'dry-run'),
|
|
4073
|
+
}));
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
4017
4076
|
if (command === 'mcp' && subcommand === 'tools') {
|
|
4018
4077
|
stderr.write('Discovering AssetHub MCP tools…\n');
|
|
4019
4078
|
const report = await diagnose({
|
|
@@ -4050,16 +4109,7 @@ const run = async () => {
|
|
|
4050
4109
|
if (command === 'mcp') {
|
|
4051
4110
|
if (subcommand !== 'config')
|
|
4052
4111
|
throw new Error('Use mcp tools [tool-name] or mcp config --client cursor|codex');
|
|
4053
|
-
const
|
|
4054
|
-
const explicitProfile = getFlag(parsed.flags, 'profile');
|
|
4055
|
-
const stored = config.profiles?.[explicitProfile ?? config.defaultProfile ?? defaultProfileName];
|
|
4056
|
-
if (explicitProfile && !stored)
|
|
4057
|
-
throw new Error('Selected profile does not exist');
|
|
4058
|
-
const selected = explicitProfile || stored?.workspaceId || isPersonalProfile(stored);
|
|
4059
|
-
const baseUrl = getFlag(parsed.flags, 'base-url') ??
|
|
4060
|
-
(selected ? stored?.baseUrl : env.ASSETHUB_API_BASE_URL ?? stored?.baseUrl) ?? defaultBaseUrl;
|
|
4061
|
-
const workspaceId = getFlag(parsed.flags, 'workspace') ??
|
|
4062
|
-
(stored && new URL(baseUrl).origin === new URL(stored.baseUrl).origin ? stored.workspaceId : undefined);
|
|
4112
|
+
const { baseUrl, workspaceId } = await resolveMcpTarget(parsed.flags);
|
|
4063
4113
|
stdout.write(mcpConfig(requireFlag(parsed.flags, 'client'), baseUrl, hasFlag(parsed.flags, 'account'), workspaceId));
|
|
4064
4114
|
return;
|
|
4065
4115
|
}
|
package/dist/setup.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
export declare const cliVersion: () => Promise<string>;
|
|
3
|
+
export declare const validatedBaseUrl: (baseUrl: string) => string;
|
|
3
4
|
export declare const mcpConfig: (client: string, baseUrl: string, account?: boolean, workspaceId?: string) => string;
|
|
4
5
|
type Check = {
|
|
5
6
|
name: 'api' | 'mcp' | 'configuration';
|
package/dist/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,oCAAoC,CAAA;AAE5D,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,MAAM,CAKvC,CAAA;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,oCAAoC,CAAA;AAE5D,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,MAAM,CAKvC,CAAA;AAEX,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,MAelD,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,SAAS,MAAM,EAAE,iBAAe,EAAE,cAAc,MAAM,KAAG,MAsBlG,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;IAClB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;CACf,CAAA;AAED,KAAK,IAAI,GAAG;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAC,CAAA;AA2DhI,eAAO,MAAM,QAAQ,GAAU,gEAM5B;IACD,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAChC,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;;;;;;;;;;;;;;;;;;;;;;EAkHA,CAAA"}
|
package/dist/setup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { AssetHubApiError, createAssetHubClient, createWorkspaceClient, WorkspaceClientError } from '@assethub/api-client';
|
|
3
3
|
export const cliVersion = async () => JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')).version;
|
|
4
|
-
const validatedBaseUrl = (baseUrl) => {
|
|
4
|
+
export const validatedBaseUrl = (baseUrl) => {
|
|
5
5
|
const url = new URL(baseUrl);
|
|
6
6
|
if (url.username || url.password || url.search || url.hash)
|
|
7
7
|
throw new Error('Use an API URL without credentials, query, or fragment.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assethub/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "Command line interface for AssetHub.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"dist",
|
|
20
|
+
"skills",
|
|
20
21
|
"README.md",
|
|
21
22
|
"LICENSE"
|
|
22
23
|
],
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"access": "public"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@assethub/api-client": "^0.1.
|
|
34
|
+
"@assethub/api-client": "^0.1.13",
|
|
34
35
|
"@modelcontextprotocol/sdk": "^1.30.0"
|
|
35
36
|
},
|
|
36
37
|
"repository": {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: assethub
|
|
3
|
+
description: |
|
|
4
|
+
Use the AssetHub CLI (`assethub`) and hosted MCP to generate and process 3D assets: images, meshes, parts, rigs, animations, textures, and full production runs. AssetHub handles the AI providers, credits, and job tracking.
|
|
5
|
+
|
|
6
|
+
TRIGGER when:
|
|
7
|
+
- Generating or editing an image, concept, or reference for a 3D asset
|
|
8
|
+
- Turning an image or prompt into a 3D mesh, splitting it into parts, or composing parts
|
|
9
|
+
- Rigging, animating, retopologizing, texturing, or converting a mesh
|
|
10
|
+
- Running a 3D production pipeline, checking a job, or recovering a run
|
|
11
|
+
- Asking what models or operations are available, or what something will cost
|
|
12
|
+
- Recording whether a generated asset is acceptable
|
|
13
|
+
|
|
14
|
+
DO NOT TRIGGER for:
|
|
15
|
+
- Installing or configuring the CLI (use `assethub init`, `assethub auth login`)
|
|
16
|
+
- Managing workspace members (use `assethub workspace ...`)
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# AssetHub CLI
|
|
20
|
+
|
|
21
|
+
`assethub` is a client, not an agent. It calls the AssetHub API, prints one JSON object to stdout, and puts progress on stderr. You decide what to do; it does it and tells you what happened.
|
|
22
|
+
|
|
23
|
+
## Output contract
|
|
24
|
+
|
|
25
|
+
- stdout is exactly one JSON object. Parse it. Never scrape stderr.
|
|
26
|
+
- Exit codes: `0` accepted or completed · `1` terminal failure · `2` input, auth, capability, or budget error · `3` timeout, history pending, or needs review · `130` interrupted.
|
|
27
|
+
- **Exit 3 is not a failure.** The server-side job keeps running. The JSON still carries `runId` and `operationId`; use them to resume or watch instead of starting again.
|
|
28
|
+
- Errors arrive as `{"error": {"code", "message"}}` plus any known `runId` / `operationId`.
|
|
29
|
+
|
|
30
|
+
## The ritual
|
|
31
|
+
|
|
32
|
+
Always in this order. Skipping a step is how credits get wasted.
|
|
33
|
+
|
|
34
|
+
### 1. Check what this key can do
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
assethub capabilities
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Tells you which operations, models, and history features this workspace key allows. If it fails with a missing-key error, the user needs `assethub auth login --api-key-stdin` (never pass a key as an argument) or `ASSETHUB_API_KEY` in the environment. A personal key also needs `assethub workspace use <workspace-id>`.
|
|
41
|
+
|
|
42
|
+
### 2. Find the operation
|
|
43
|
+
|
|
44
|
+
Prefer the dedicated commands (`image generate`, `mesh generate`, `parts split`, `rig create`, `animate retarget`, `production analyze` …). For anything else, search the live catalog:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
assethub api search "compose parts"
|
|
48
|
+
assethub api describe "POST /mesh/compose"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`describe` returns the exact input schema and required fields. **Read it before you call.** Do not guess parameter names.
|
|
52
|
+
|
|
53
|
+
### 3. Pick the model deliberately
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
assethub models list --domain mesh
|
|
57
|
+
assethub models get <model-id>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The default model is not always the right one. A model's catalog entry carries its credit plan. If the user has already tried a model and it failed, choose a different one rather than retrying the same thing.
|
|
61
|
+
|
|
62
|
+
### 4. Run, with an operation ID for anything paid
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
assethub image generate --prompt "stylized wooden crate" --wait --download --out-dir ./out/crate
|
|
66
|
+
assethub mesh generate --source-id <image-asset-id> --canvas <canvas-id> --wait
|
|
67
|
+
assethub api call "POST /mesh/compose" --input-json @request.json --operation-id $(uuidgen)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- `--wait` blocks until the job finishes and returns the final result. Without it you get a job or run ID to watch.
|
|
71
|
+
- `--download --out-dir <dir>` fetches every output artifact and writes a `manifest.json`.
|
|
72
|
+
- `--operation-id <uuid>` makes a paid request idempotent. Reuse the **same** ID with the **same** input to retry; a new candidate needs a new ID.
|
|
73
|
+
- Generation returns `execution` with the canvas URL, run and job IDs, and the output asset IDs. Chain those IDs into the next step with `--source-id`.
|
|
74
|
+
|
|
75
|
+
### 5. Wait or recover — never resubmit blindly
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
assethub jobs watch <job-id> --download --out-dir ./out
|
|
79
|
+
assethub runs watch <run-id>
|
|
80
|
+
assethub runs resume <operation-id> --wait
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
After a timeout, an interrupted command, or any uncertain response, `runs resume <operation-id>` replays the identical request and returns the existing result. It never charges twice. Starting a fresh command does.
|
|
84
|
+
|
|
85
|
+
### 6. Record the verdict
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
assethub evaluations submit --canvas <canvas-id> --artifact <asset-id> \
|
|
89
|
+
--report ./review.json --agent <your-name> --require-pass
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`review.json` must have `schemaVersion: "assethub.evaluation-submission.v1"`, a `rubric`, a `verdict` of `pass` | `fail` | `needs_review`, `criteria`, `referenceAssetIds`, and `evidenceAssetIds`. Inspect the downloaded artifact before you write the verdict; a verdict you did not check is worse than none. `--require-pass` exits 1 on fail and 3 on needs_review, so a pipeline can stop on a bad result. An agent verdict is never creator approval.
|
|
93
|
+
|
|
94
|
+
## Cost safety
|
|
95
|
+
|
|
96
|
+
- `production run` defaults to `full_auto`, which never pauses for a human. **Always pass `--max-cost-credits <n>`.** The server caps `--max-iterations` at 5.
|
|
97
|
+
- Over MCP the same rule applies: always set `maxCostCredits` on `production_run`.
|
|
98
|
+
- `assethub account get` shows the balance. Check it before a batch.
|
|
99
|
+
- Prefer a dedicated, scoped API key for agent work rather than a full-access one.
|
|
100
|
+
|
|
101
|
+
## Rules learned the hard way
|
|
102
|
+
|
|
103
|
+
- Never put an API key in a command argument or a config file. Use `--api-key-stdin` or the environment.
|
|
104
|
+
- Part extractor names are the public product names: `V1.5`, `V2.0 alpha`, `V2.1 alpha`. Internal IDs are not accepted.
|
|
105
|
+
- `parts split` and `parts compare` with `--preprocess-prompt` create a second production order; use `--all-ready`, not `--task-id`.
|
|
106
|
+
- Every generation should land on a canvas. Pass `--canvas <id>` to keep a job's steps together; without it the CLI makes one canvas per working directory. `assethub canvas open <id>` shows the user what happened.
|
|
107
|
+
- A complaint like "the mesh has extra limbs" is usually an input problem: stray lines, shadows, or inconsistent views. Clean the image (`image edit`) before switching models.
|
|
108
|
+
- Input can come from a file, a URL, stdin bytes, base64, a data URI, an OpenAI- or Anthropic-style JSON attachment (`--stdin-json`), or the clipboard. You never need to write a temporary file first.
|
|
109
|
+
- Large or complex requests: `--input-json @request.json` with the schema from `api describe`.
|
|
110
|
+
|
|
111
|
+
## Reusable methods (workspace skills)
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
assethub skills list
|
|
115
|
+
assethub skills get <skill-id>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
A workspace skill is a saved method that already worked in this workspace. Read them before inventing a new approach to a problem the team has solved. Skills set to `automatic` apply on matching runs without asking.
|
|
119
|
+
|
|
120
|
+
## MCP equivalents
|
|
121
|
+
|
|
122
|
+
The hosted MCP (`https://app.assethub.io/api/mcp`) exposes the same API. `capabilities_get` ↔ `capabilities`; `operation_search` / `operation_describe` / `operation_call` ↔ `api search` / `describe` / `call`; `job_poll` ↔ `jobs watch`; `evaluation_submit` ↔ `evaluations submit`. Whichever surface you use, follow the same ritual.
|
|
123
|
+
|
|
124
|
+
## Diagnose
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
assethub doctor --mcp
|
|
128
|
+
assethub --version
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`doctor` checks the key, the workspace, and MCP tool discovery without spending credits. Exit 2 means a check failed and the JSON says which and what to do.
|