@assethub/cli 0.1.35 → 0.1.36

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
@@ -62,6 +62,29 @@ print the `export ASSETHUB_API_KEY=...` line, and export it in the shell that
62
62
  starts Claude Code or Codex. The key is redacted
63
63
  (`ah_…last4`) everywhere else.
64
64
 
65
+ ## Staying up to date
66
+
67
+ ```sh
68
+ assethub update --check # JSON: up_to_date or update_available
69
+ assethub update # upgrade a global install now; the saved login is kept
70
+ assethub setup --no-auto-update # turn background updates off (--auto-update turns them on)
71
+ ```
72
+
73
+ Auto-update is on by default. Once a day, a detached background process checks
74
+ for a newer release and installs it with the package manager that installed the
75
+ CLI, the same way `assethub update --yes` does, keeping the saved login. No
76
+ command waits for it, and an install never happens inside a running command; the
77
+ next command prints one line on stderr (`updated itself from 0.1.36 to 0.1.37`).
78
+ stdout stays one JSON object. If the install fails, the CLI keeps the current
79
+ version and says so, then keeps announcing the newer release.
80
+
81
+ It applies only to a global install (not `npx`, a project dependency or a source
82
+ checkout) and is off in CI, with `ASSETHUB_NO_AUTO_UPDATE=1` (no install, notice
83
+ only) or with `ASSETHUB_NO_UPDATE_CHECK=1` (no check at all). The choice is saved
84
+ in `~/.assethub/settings.json`, and `assethub setup` shows it as its `auto-update`
85
+ step. The bundled agent skill also asks agents to run `assethub update --check`
86
+ at the start of each session.
87
+
65
88
  ## API discovery and Workspace Skills
66
89
 
67
90
  Use `api search [query]` to discover the authenticated server catalog and
@@ -27,7 +27,7 @@ export declare const readJsonServer: (text: string | undefined) => unknown;
27
27
  * Returns the new text, or the input unchanged when the entry already matches.
28
28
  */
29
29
  export declare const mergeJsonServer: (path: string, text: string | undefined, entry: Record<string, unknown>) => string;
30
- export type SkillStatus = 'installed' | 'updated' | 'unchanged' | 'kept-existing';
30
+ export type SkillStatus = 'installed' | 'updated' | 'unchanged' | 'kept-existing' | 'kept-newer';
31
31
  export declare const installSkill: ({ root, version, dryRun, }: {
32
32
  /** The home folder, or the project folder for a project install. */
33
33
  root: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agentSetup.d.ts","sourceRoot":"","sources":["../src/agentSetup.ts"],"names":[],"mappings":"AAiCA,eAAO,MAAM,SAAS,6CAA8C,CAAA;AACpE,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhD,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;CACtC,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAyB3C,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,IAAI,OAAO,KAAG,SACiB,CAAA;AAEzD,sHAAsH;AACtH,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,EAAE,KAAG,OAAO,EAOvD,CAAA;AAED,qHAAqH;AACrH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,YAAY,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,EACzD,YAAY,MAAM,KACjB,OAAO,CAAC,OAAO,EAAE,CAUnB,CAAA;AAMD,+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,+GAA+G;AAC/G,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,OAQzD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,MAAM,EACZ,MAAM,MAAM,GAAG,SAAS,EACxB,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MA2BF,CAAA;AAID,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,eAAe,CAAA;AAmCjF,eAAO,MAAM,YAAY,GAAU,4BAIhC;IACD,oEAAoE;IACpE,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,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,eAAe,CAAA;AAkC9E;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,IAAI,CAevD,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAC,EAAE,CAAA;CAC5D,CAAA;AAED,+GAA+G;AAC/G,eAAO,MAAM,kBAAkB,GAAU,2BAItC;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,EAAE,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;CAChB,KAAG,OAAO,CAAC,YAAY,CAMvB,CAAA"}
1
+ {"version":3,"file":"agentSetup.d.ts","sourceRoot":"","sources":["../src/agentSetup.ts"],"names":[],"mappings":"AAkCA,eAAO,MAAM,SAAS,6CAA8C,CAAA;AACpE,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhD,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;CACtC,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAyB3C,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,IAAI,OAAO,KAAG,SACiB,CAAA;AAEzD,sHAAsH;AACtH,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,EAAE,KAAG,OAAO,EAOvD,CAAA;AAED,qHAAqH;AACrH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,YAAY,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,EACzD,YAAY,MAAM,KACjB,OAAO,CAAC,OAAO,EAAE,CAUnB,CAAA;AAMD,+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,+GAA+G;AAC/G,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,OAQzD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,MAAM,EACZ,MAAM,MAAM,GAAG,SAAS,EACxB,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MA2BF,CAAA;AAID,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,eAAe,GAAG,YAAY,CAAA;AAmChG,eAAO,MAAM,YAAY,GAAU,4BAIhC;IACD,oEAAoE;IACpE,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,CA2BhE,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,eAAe,CAAA;AAkC9E;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,IAAI,CAgBvD,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAC,EAAE,CAAA;CAC5D,CAAA;AAED,+GAA+G;AAC/G,eAAO,MAAM,kBAAkB,GAAU,2BAItC;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,EAAE,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;CAChB,KAAG,OAAO,CAAC,YAAY,CAMvB,CAAA"}
@@ -19,6 +19,7 @@ import { dirname, join, relative, resolve } from 'node:path';
19
19
  import { env, pid } from 'node:process';
20
20
  import { fileURLToPath } from 'node:url';
21
21
  import { cliVersion, validatedBaseUrl } from './setup.js';
22
+ import { compareVersions } from './update.js';
22
23
  export const AGENT_IDS = ['claude-code', 'codex', 'cursor'];
23
24
  export const AGENT_SPECS = [
24
25
  {
@@ -191,13 +192,16 @@ export const installSkill = async ({ root, version, dryRun, }) => {
191
192
  : 'installed'
192
193
  : marker === version
193
194
  ? 'unchanged'
194
- : 'updated';
195
+ : // An older CLI (or a stale monorepo build) never replaces a newer skill.
196
+ compareVersions(version, marker) < 0
197
+ ? 'kept-newer'
198
+ : 'updated';
195
199
  if (!dryRun && (status === 'installed' || status === 'updated'))
196
200
  await replaceSkillDir(source, target, version);
197
201
  return {
198
202
  path: target,
199
203
  status,
200
- ...(status === 'kept-existing' ? {} : { version }),
204
+ ...(status === 'kept-existing' ? {} : { version: status === 'kept-newer' ? marker : version }),
201
205
  };
202
206
  };
203
207
  const linkSkill = async (agentSkillsDir, canonical, dryRun) => {
@@ -244,7 +248,8 @@ export const syncInstalledSkill = async () => {
244
248
  if (installed === undefined)
245
249
  return;
246
250
  const version = await cliVersion();
247
- if (installed === version)
251
+ // Only an upgrade refreshes it: an older CLI never overwrites a newer skill.
252
+ if (installed === version || compareVersions(version, installed) < 0)
248
253
  return;
249
254
  const source = packagedSkillDir();
250
255
  if (!(await isDirectory(source)))
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ type CommandContext = {
24
24
  */
25
25
  args: string[];
26
26
  };
27
- 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 setup [--agent claude-code|codex|cursor...] [--project] [--only <step,...>] [--skip <step,...>] [--no-skills] [--profile <name>] [--workspace <id>] [--api-key-stdin] [--base-url <url>] [--no-app-env] [--dry-run] [--yes] [--print-env] [--json]\n assethub env load [--profile <name>]\n assethub update [--check] [--dry-run] [--yes]\n assethub doctor [--mcp] [--setup [--agent <name>...] [--project]] [--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 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] [--effort light|standard|thorough] [--max-rounds <n>] [--agent-model <provider:id|unambiguous-id>] [--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 refine --from-run <run-id> --skill-assembly [--reasoning low|medium|high] [--agent-model <agents-api model>] [--wait]\n assethub composer run --part <mesh-asset-id>[=<part-image-id>] [--part <mesh-asset-id>[=<part-image-id>]...] --reference <image-asset-id> [--model <id>] [--agent-model <provider:id|unambiguous-id>] [--mode quick|quality] [--optimize off|light|medium|heavy] [--transforms-json <json|@file>] [--canvas <id>] [--wait]\n assethub composer run --part <mesh-asset-id>=<part-image-id>... --reference <image-asset-id> --model V6 [--optimize off|light|medium|heavy] [--canvas <id>] [--wait]\n assethub composer run --part <mesh-asset-id>... --reference <image-asset-id> --model V5.1 [--reasoning low|medium|high] [--agent-model <agents-api model>] [--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 canvas graph-id <canvas-id>\n assethub runs list [--canvas <id>] [--cursor <cursor>] [--limit <n>]\n assethub runs get|watch|wait <run-id> [--interval-ms <ms>] [--timeout-ms <n>] [--summary] [--download --out-dir <dir>]\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 graph snapshot <graph-id> [--offset <n>] [--limit <n>]\n assethub graph node <graph-id> <node-id>\n assethub graph image <graph-id> <node-id> [--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>] [--base-body <mesh-asset-id>] [--skill-planner-model <model>] [--auto-repair true|false] [--skill-mode auto|manual|off] [--skill-id <id>...] [--context <canvas-id> --context-version <n>] [--wait] [--download --out-dir <dir>]\n Production graph options: --pipeline-depth parts|mesh|composition [--assembly-policy concept-to-character-v1] [--mesh-generation-json <json|@file>]\n assethub production batch (--file <path>... | --files-dir <dir> | --source-id <id>... | --source-url <url>...) --part-extractor <name> [--repeat <n>] [--yes] [--canvas <id>] [--operation-id <uuid>] [--wait [--concurrency <n>]] [--download --out-dir <dir>]\n One production analyze per image \u00D7 --repeat, on one canvas. Takes the production analyze options. Rerun with the printed --operation-id to resume; nothing is started twice.\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 run --image <file|asset-id> [--canvas <id>] [--part-extractor <name>] [--compose v6|none] [--mesh-model <id>] [--max-parts <n>] [--max-cost <credits>] [--estimate] [--wait] [--download --out-dir <dir>] [--json]\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 production resume <run-id|order-id> [--expected-resume-count <n>] [--wait] [--timeout-ms <ms>] [--download --out-dir <dir>] (after a fix, pick a stopped or stalled graph run back up; no new charge)\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>] [--base-body <mesh-asset-id>] [--skill-planner-model <model>] [--auto-repair true|false] [--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 assethub memory memorize --canvas <id> [--nodes <shape-id>,<shape-id>,...] [--wait] [--timeout <s>]\n assethub memory replay <memory-id> --source <asset-id> [--wait] [--timeout <s>]\n assethub skills list [--cursor <cursor>]\n assethub skills get <skill-id> [--revision <n>]\n assethub skills memory <skill-id> [--revision <n>] [--images <dir>]\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 skills validate --input-json <json|@file|@-> [--build <build-id>]\n assethub skills schema\n assethub skills build --goal <text> (--canvas <id>... | --memory <id>...) [--instructions <text>] [--operation-id <uuid>] [--wait] [--dry-run]\n assethub skills official list\n assethub skills official install <skill-id> --revision <n> --content-sha256 <sha256> [--expected-revision <n>] [--expected-grant-revision <n>]\n assethub skills official prepare <skill-id> --input-json <json|@file|@->\n assethub skills official customize <skill-id> --input-json <json|@file|@->\n assethub org search <query>\n assethub org canvases --org <orgId>\n assethub graph export-canvas --org <orgId> --canvas <id>[,<id>...] [--wait] [--timeout <s>] [--out-dir <dir>]\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 setup --dry-run\n assethub setup --only skills --project\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 analyze --file ./concept.png --part-extractor v4 --wait --download --out-dir ./character\n assethub production batch --file ./harpy.png --file ./satyr.png --repeat 2 --part-extractor v4 --yes --wait --download --out-dir ./batch\n assethub production run --order-id ord_x --mission-id ms_x --run-mode full_auto --max-cost-credits 200 --wait\n assethub production run --image ./character.png --estimate\n assethub production run --image ./character.png --compose v6 --max-cost 500 --wait --download --out-dir ./out/asset\n assethub production watch ord_x\n assethub runs get run_x --summary\n assethub runs wait run_x --timeout-ms 3600000\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 production resume ord_x\n assethub production resume <run-id> --wait --download --out-dir ./character\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";
27
+ 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 setup [--agent claude-code|codex|cursor...] [--project] [--only <step,...>] [--skip <step,...>] [--no-skills] [--no-auto-update | --auto-update] [--profile <name>] [--workspace <id>] [--api-key-stdin] [--base-url <url>] [--no-app-env] [--dry-run] [--yes] [--print-env] [--json]\n assethub env load [--profile <name>]\n assethub update [--check] [--dry-run] [--yes] (once a day a newer version installs itself in the background; turn off: setup --no-auto-update or ASSETHUB_NO_AUTO_UPDATE=1)\n assethub doctor [--mcp] [--setup [--agent <name>...] [--project]] [--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 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] [--effort light|standard|thorough] [--max-rounds <n>] [--agent-model <provider:id|unambiguous-id>] [--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 refine --from-run <run-id> --skill-assembly [--reasoning low|medium|high] [--agent-model <agents-api model>] [--wait]\n assethub composer run --part <mesh-asset-id>[=<part-image-id>] [--part <mesh-asset-id>[=<part-image-id>]...] --reference <image-asset-id> [--model <id>] [--agent-model <provider:id|unambiguous-id>] [--mode quick|quality] [--optimize off|light|medium|heavy] [--transforms-json <json|@file>] [--canvas <id>] [--wait]\n assethub composer run --part <mesh-asset-id>=<part-image-id>... --reference <image-asset-id> --model V6 [--optimize off|light|medium|heavy] [--canvas <id>] [--wait]\n assethub composer run --part <mesh-asset-id>... --reference <image-asset-id> --model V5.1 [--reasoning low|medium|high] [--agent-model <agents-api model>] [--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 canvas graph-id <canvas-id>\n assethub runs list [--canvas <id>] [--cursor <cursor>] [--limit <n>]\n assethub runs get|watch|wait <run-id> [--interval-ms <ms>] [--timeout-ms <n>] [--summary] [--download --out-dir <dir>]\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 graph snapshot <graph-id> [--offset <n>] [--limit <n>]\n assethub graph node <graph-id> <node-id>\n assethub graph image <graph-id> <node-id> [--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>] [--base-body <mesh-asset-id>] [--skill-planner-model <model>] [--auto-repair true|false] [--skill-mode auto|manual|off] [--skill-id <id>...] [--context <canvas-id> --context-version <n>] [--wait] [--download --out-dir <dir>]\n Production graph options: --pipeline-depth parts|mesh|composition [--assembly-policy concept-to-character-v1] [--mesh-generation-json <json|@file>]\n assethub production batch (--file <path>... | --files-dir <dir> | --source-id <id>... | --source-url <url>...) --part-extractor <name> [--repeat <n>] [--yes] [--canvas <id>] [--operation-id <uuid>] [--wait [--concurrency <n>]] [--download --out-dir <dir>]\n One production analyze per image \u00D7 --repeat, on one canvas. Takes the production analyze options. Rerun with the printed --operation-id to resume; nothing is started twice.\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 run --image <file|asset-id> [--canvas <id>] [--part-extractor <name>] [--compose v6|none] [--mesh-model <id>] [--max-parts <n>] [--max-cost <credits>] [--estimate] [--wait] [--download --out-dir <dir>] [--json]\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 production resume <run-id|order-id> [--expected-resume-count <n>] [--wait] [--timeout-ms <ms>] [--download --out-dir <dir>] (after a fix, pick a stopped or stalled graph run back up; no new charge)\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>] [--base-body <mesh-asset-id>] [--skill-planner-model <model>] [--auto-repair true|false] [--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 assethub memory memorize --canvas <id> [--nodes <shape-id>,<shape-id>,...] [--wait] [--timeout <s>]\n assethub memory replay <memory-id> --source <asset-id> [--wait] [--timeout <s>]\n assethub skills list [--cursor <cursor>]\n assethub skills get <skill-id> [--revision <n>]\n assethub skills memory <skill-id> [--revision <n>] [--images <dir>]\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 skills validate --input-json <json|@file|@-> [--build <build-id>]\n assethub skills schema\n assethub skills build --goal <text> (--canvas <id>... | --memory <id>...) [--instructions <text>] [--operation-id <uuid>] [--wait] [--dry-run]\n assethub skills official list\n assethub skills official install <skill-id> --revision <n> --content-sha256 <sha256> [--expected-revision <n>] [--expected-grant-revision <n>]\n assethub skills official prepare <skill-id> --input-json <json|@file|@->\n assethub skills official customize <skill-id> --input-json <json|@file|@->\n assethub org search <query>\n assethub org canvases --org <orgId>\n assethub graph export-canvas --org <orgId> --canvas <id>[,<id>...] [--wait] [--timeout <s>] [--out-dir <dir>]\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 setup --dry-run\n assethub setup --only skills --project\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 analyze --file ./concept.png --part-extractor v4 --wait --download --out-dir ./character\n assethub production batch --file ./harpy.png --file ./satyr.png --repeat 2 --part-extractor v4 --yes --wait --download --out-dir ./batch\n assethub production run --order-id ord_x --mission-id ms_x --run-mode full_auto --max-cost-credits 200 --wait\n assethub production run --image ./character.png --estimate\n assethub production run --image ./character.png --compose v6 --max-cost 500 --wait --download --out-dir ./out/asset\n assethub production watch ord_x\n assethub runs get run_x --summary\n assethub runs wait run_x --timeout-ms 3600000\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 production resume ord_x\n assethub production resume <run-id> --wait --download --out-dir ./character\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";
28
28
  export declare const commandSetup: (flags: Flags, installHooks?: InstallHooks, overrides?: {
29
29
  skip?: SetupStepName[];
30
30
  }) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { launchAgentProgram, loadKeyIntoLaunchd } from './appEnv.js';
8
8
  import { defaultInstallHooks } from './setupHooksBridge.js';
9
9
  import { canUploadSessions, installHooks as installSessionHooks, runHooksCommand } from './hooks/index.js';
10
10
  import { createNodeUpdateDeps, runUpdate } from './update.js';
11
+ import { autoUpdateEnabled, claimRefreshAttempt, decideUpdateNotice, isInstalledPackage, patchUpdateCheck, readAutoUpdateSetting, readUpdateCheck, REFRESH_CHILD_ENV, refreshInBackground, refreshUpdateCheck, settingsPath, takeLock, updateCheckDisabled, updateCheckPath, writeAutoUpdateSetting, writeUpdateCheck, } from './updateNotice.js';
11
12
  import { ingestProjectSources } from './projectSources.js';
12
13
  import { downloadCanvas } from './canvasDownload.js';
13
14
  import { downloadCanvasGraphZip } from './canvasGraphDownload.js';
@@ -18,7 +19,7 @@ import { importCanvasAssetWithState } from './canvasAssetImport.js';
18
19
  import { execFile, spawn } from 'node:child_process';
19
20
  import { createHash, randomUUID } from 'node:crypto';
20
21
  import { realpathSync } from 'node:fs';
21
- import { chmod, mkdir, readdir, readFile, rename, unlink, writeFile, } from 'node:fs/promises';
22
+ import { chmod, mkdir, readdir, readFile, rename, rm, unlink, writeFile, } from 'node:fs/promises';
22
23
  import { homedir, tmpdir } from 'node:os';
23
24
  import { basename, dirname, extname, join, resolve } from 'node:path';
24
25
  import { argv, env, exit, stdin, stderr, stdout } from 'node:process';
@@ -222,9 +223,9 @@ CLI and MCP are available to all AssetHub users. Workspace permissions and featu
222
223
 
223
224
  Usage:
224
225
  assethub --version
225
- assethub setup [--agent claude-code|codex|cursor...] [--project] [--only <step,...>] [--skip <step,...>] [--no-skills] [--profile <name>] [--workspace <id>] [--api-key-stdin] [--base-url <url>] [--no-app-env] [--dry-run] [--yes] [--print-env] [--json]
226
+ assethub setup [--agent claude-code|codex|cursor...] [--project] [--only <step,...>] [--skip <step,...>] [--no-skills] [--no-auto-update | --auto-update] [--profile <name>] [--workspace <id>] [--api-key-stdin] [--base-url <url>] [--no-app-env] [--dry-run] [--yes] [--print-env] [--json]
226
227
  assethub env load [--profile <name>]
227
- assethub update [--check] [--dry-run] [--yes]
228
+ assethub update [--check] [--dry-run] [--yes] (once a day a newer version installs itself in the background; turn off: setup --no-auto-update or ASSETHUB_NO_AUTO_UPDATE=1)
228
229
  assethub doctor [--mcp] [--setup [--agent <name>...] [--project]] [--profile <name>] [--timeout-ms <n>]
229
230
  assethub mcp tools [tool-name] [--account] [--profile <name>] [--timeout-ms <n>]
230
231
  assethub mcp config --client cursor|codex [--account] [--base-url <url>]
@@ -1925,7 +1926,7 @@ const parseSetupSelection = (flags) => {
1925
1926
  const known = (name, values) => {
1926
1927
  const unknown = values.filter(value => !SETUP_STEPS.includes(value));
1927
1928
  if (unknown.length > 0)
1928
- throw new Error(`Unknown setup step for --${name}: ${unknown.join(', ')}. Steps: login, workspace, mcp, skills, app-env, doctor.`);
1929
+ throw new Error(`Unknown setup step for --${name}: ${unknown.join(', ')}. Steps: login, workspace, mcp, skills, auto-update, app-env, doctor.`);
1929
1930
  return values;
1930
1931
  };
1931
1932
  const only = known('only', stepValues('only'));
@@ -1973,6 +1974,7 @@ const setupRun = async (flags, installHooks, overrides = {}) => {
1973
1974
  apiKeySource: flagKey ? 'flag' : envKey ? 'env' : undefined,
1974
1975
  noHook: hasFlag(flags, 'no-hook'),
1975
1976
  noAppEnv: hasFlag(flags, 'no-app-env'),
1977
+ ...(hasFlag(flags, 'no-auto-update') ? { autoUpdate: false } : hasFlag(flags, 'auto-update') ? { autoUpdate: true } : {}),
1976
1978
  saveSessions: hasFlag(flags, 'save-sessions'),
1977
1979
  dryRun: overrides.dryRun ?? hasFlag(flags, 'dry-run'),
1978
1980
  yes: hasFlag(flags, 'yes'),
@@ -2012,6 +2014,10 @@ const setupRun = async (flags, installHooks, overrides = {}) => {
2012
2014
  return commandAuth('login', loginFlags);
2013
2015
  }),
2014
2016
  resolveTarget: () => resolveMcpTarget(flags),
2017
+ readAutoUpdate: () => readAutoUpdateSetting(settingsPath(getConfigPath(flags))),
2018
+ writeAutoUpdate: on => writeAutoUpdateSetting(settingsPath(getConfigPath(flags)), on),
2019
+ canSelfUpdate: async () => isInstalledPackage(realpathSync(argv[1] ?? fileURLToPath(import.meta.url))),
2020
+ autoUpdateBlockedByEnv: !autoUpdateEnabled(undefined, env),
2015
2021
  resolveAuth: async () => {
2016
2022
  const auth = await resolveAuth(base);
2017
2023
  return { apiKey: auth.apiKey, baseUrl: auth.baseUrl, profile: auth.profile, workspaceId: auth.workspaceId };
@@ -2066,7 +2072,7 @@ export const commandSetup = async (flags, installHooks = defaultInstallHooks, ov
2066
2072
  */
2067
2073
  const setupCheck = async (flags) => {
2068
2074
  const { options, deps } = await setupRun(flags, installSessionHooks, {
2069
- skip: ['login', 'workspace', 'app-env', 'hook', 'doctor'],
2075
+ skip: ['login', 'workspace', 'auto-update', 'app-env', 'hook', 'doctor'],
2070
2076
  dryRun: true,
2071
2077
  quietLog: true,
2072
2078
  });
@@ -5649,6 +5655,33 @@ const resolveMcpTarget = async (flags) => {
5649
5655
  (stored && new URL(baseUrl).origin === new URL(stored.baseUrl).origin ? stored.workspaceId : undefined);
5650
5656
  return { baseUrl, workspaceId };
5651
5657
  };
5658
+ // A stderr line when a newer CLI is known or was just installed, from a cache
5659
+ // refreshed (and, with auto-update on, acted on) at most once a day in the background.
5660
+ const noticeUpdate = async (flags) => {
5661
+ if (updateCheckDisabled(env) || env[REFRESH_CHILD_ENV])
5662
+ return;
5663
+ try {
5664
+ const cli = realpathSync(argv[1] ?? fileURLToPath(import.meta.url));
5665
+ if (!isInstalledPackage(cli))
5666
+ return;
5667
+ const cachePath = updateCheckPath(getConfigPath(flags));
5668
+ const cache = await readUpdateCheck(cachePath);
5669
+ const now = new Date();
5670
+ const decision = decideUpdateNotice(await cliVersion(), cache, now);
5671
+ for (const notice of decision.notices)
5672
+ stderr.write(`${notice}\n`);
5673
+ if (cache && (decision.markAnnounced || decision.markNoticed))
5674
+ await patchUpdateCheck(cachePath, cache, {
5675
+ ...(decision.markAnnounced && cache.autoUpdate ? { autoUpdate: { ...cache.autoUpdate, announced: true } } : {}),
5676
+ ...(decision.markNoticed ? { noticedAt: now.toISOString() } : {}),
5677
+ });
5678
+ if (decision.refresh && (await claimRefreshAttempt(cachePath, now)))
5679
+ refreshInBackground(process.execPath, cli, getConfigPath(flags));
5680
+ }
5681
+ catch {
5682
+ // best effort
5683
+ }
5684
+ };
5652
5685
  const run = async () => {
5653
5686
  const parsed = parseArgs(argv.slice(2));
5654
5687
  if (hasFlag(parsed.flags, 'version')) {
@@ -5662,13 +5695,15 @@ const run = async () => {
5662
5695
  const [command, subcommand] = parsed.positionals;
5663
5696
  if (command !== 'init')
5664
5697
  await syncInstalledSkill();
5698
+ if (!['update', 'hooks', 'env'].includes(command ?? ''))
5699
+ await noticeUpdate(parsed.flags);
5665
5700
  if (command === 'init') {
5666
5701
  if (subcommand)
5667
5702
  throw new Error(`Use init [--agent ${AGENT_IDS.join('|')}] [--project] [--dry-run]`);
5668
5703
  // Kept for existing scripts: agent wiring only, as before, through setup's steps.
5669
5704
  stderr.write('`assethub init` is deprecated and will be removed in a later version; use `assethub setup` (or `assethub setup --only mcp,skills` for agent wiring only).\n');
5670
5705
  await commandSetup({ ...parsed.flags, json: true, yes: true }, installSessionHooks, {
5671
- skip: ['login', 'workspace', 'app-env', 'hook', 'doctor'],
5706
+ skip: ['login', 'workspace', 'auto-update', 'app-env', 'hook', 'doctor'],
5672
5707
  });
5673
5708
  return;
5674
5709
  }
@@ -5735,13 +5770,36 @@ const run = async () => {
5735
5770
  return;
5736
5771
  }
5737
5772
  if (command === 'update') {
5773
+ const cachePath = updateCheckPath(getConfigPath(parsed.flags));
5774
+ if (hasFlag(parsed.flags, 'refresh-check')) {
5775
+ // The detached daily check started by noticeUpdate: record the latest
5776
+ // version and, with auto-update on, install it. Prints nothing.
5777
+ const configPath = getConfigPath(parsed.flags);
5778
+ await refreshUpdateCheck({
5779
+ cachePath,
5780
+ lockPath: `${cachePath}.lock`,
5781
+ autoUpdate: autoUpdateEnabled(await readAutoUpdateSetting(settingsPath(configPath)), env),
5782
+ deps: createNodeUpdateDeps(['--config', configPath]),
5783
+ });
5784
+ return;
5785
+ }
5738
5786
  // The verifying doctor checks the same login this command was given.
5739
5787
  const forwardFlags = ['profile', 'config', 'base-url', 'workspace'].flatMap(name => {
5740
5788
  const value = getFlag(parsed.flags, name);
5741
5789
  return value ? [`--${name}`, value] : [];
5742
5790
  });
5743
5791
  const apiKey = getFlag(parsed.flags, 'api-key');
5744
- print(await runUpdate({ check: hasFlag(parsed.flags, 'check'), dryRun: hasFlag(parsed.flags, 'dry-run'), yes: hasFlag(parsed.flags, 'yes') }, createNodeUpdateDeps(forwardFlags, apiKey ? { ASSETHUB_API_KEY: apiKey } : {})));
5792
+ const installs = !hasFlag(parsed.flags, 'check') && !hasFlag(parsed.flags, 'dry-run');
5793
+ // Never install alongside a background auto-update.
5794
+ if (installs && !(await takeLock(`${cachePath}.lock`)))
5795
+ throw new Error('A background update of the CLI is running; try again in a minute.');
5796
+ const report = await runUpdate({ check: hasFlag(parsed.flags, 'check'), dryRun: hasFlag(parsed.flags, 'dry-run'), yes: hasFlag(parsed.flags, 'yes') }, createNodeUpdateDeps(forwardFlags, apiKey ? { ASSETHUB_API_KEY: apiKey } : {})).finally(async () => {
5797
+ if (installs)
5798
+ await rm(`${cachePath}.lock`, { force: true });
5799
+ });
5800
+ // Every answer from the registry refreshes the daily hint, so it never nags about an installed version.
5801
+ await writeUpdateCheck(cachePath, report.status === 'updated' ? report.to : report.latest).catch(() => undefined);
5802
+ print(report);
5745
5803
  return;
5746
5804
  }
5747
5805
  if (command === 'doctor') {
@@ -12,7 +12,7 @@ export type SetupStep = {
12
12
  /** The config file or folder the step reads and writes. */
13
13
  path?: string;
14
14
  };
15
- export declare const SETUP_STEPS: readonly ["login", "workspace", "mcp", "skills", "app-env", "hook", "doctor"];
15
+ export declare const SETUP_STEPS: readonly ["login", "workspace", "mcp", "skills", "auto-update", "app-env", "hook", "doctor"];
16
16
  export type SetupStepName = (typeof SETUP_STEPS)[number];
17
17
  export type SetupResult = {
18
18
  ok: boolean;
@@ -47,6 +47,8 @@ export type SetupOptions = {
47
47
  noHook: boolean;
48
48
  /** `--no-app-env`: leave launchd alone even on macOS. */
49
49
  noAppEnv?: boolean;
50
+ /** `--auto-update` / `--no-auto-update`: save that choice; undefined keeps the saved one (default on). */
51
+ autoUpdate?: boolean;
50
52
  /** Explicit consent to record and upload agent sessions (`--save-sessions`). */
51
53
  saveSessions: boolean;
52
54
  dryRun: boolean;
@@ -121,6 +123,13 @@ export type SetupDeps = {
121
123
  home: string;
122
124
  /** argv the macOS LaunchAgent runs at login: node, this CLI, `env load --profile <name>`. */
123
125
  launchAgentProgram: string[];
126
+ /** The saved auto-update choice (undefined: never set, which means on). */
127
+ readAutoUpdate: () => Promise<boolean | undefined>;
128
+ writeAutoUpdate: (on: boolean) => Promise<void>;
129
+ /** Whether this CLI is an installed package `assethub update` can upgrade (not npx or a source checkout). */
130
+ canSelfUpdate: () => Promise<boolean>;
131
+ /** ASSETHUB_NO_AUTO_UPDATE, ASSETHUB_NO_UPDATE_CHECK or CI turn it off for this environment. */
132
+ autoUpdateBlockedByEnv: boolean;
124
133
  };
125
134
  export declare const redactKey: (key: string) => string;
126
135
  export declare const mergeCodexSection: (existing: string, section: string) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"setupCommand.d.ts","sourceRoot":"","sources":["../src/setupCommand.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,WAAW,EAAwE,MAAM,aAAa,CAAA;AAE9G,OAAO,EAQL,KAAK,OAAO,EACZ,KAAK,YAAY,EAClB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAA;AAEvD,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAC/C,4FAA4F;AAC5F,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,cAAc,CAAA;AACjE,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAID,eAAO,MAAM,WAAW,+EAAgF,CAAA;AACxG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,MAAM,EAAE,OAAO,EAAE,CAAA;IACjB,uEAAuE;IACvE,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;IAClB,yFAAyF;IACzF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,aAAa,EAAE,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,+GAA+G;IAC/G,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAC7B,MAAM,EAAE,OAAO,CAAA;IACf,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gFAAgF;IAChF,YAAY,EAAE,OAAO,CAAA;IACrB,MAAM,EAAE,OAAO,CAAA;IACf,GAAG,EAAE,OAAO,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,CAAA;AAExD,KAAK,YAAY,GAAG;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC,EAAE,CAAA;CAAC,CAAA;AAI7F,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,YAAY,CAAA;IAC1B,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,IAAI,CAAA;IACf,aAAa,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IACrC,KAAK,EAAE,CAAC,KAAK,EAAE;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACnC,WAAW,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,CAAA;IACrC,6GAA6G;IAC7G,aAAa,EAAE,MAAM,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IACrE,wCAAwC;IACxC,YAAY,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACtC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9B,wGAAwG;IACxG,aAAa,EAAE,CAAC,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAC,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;IACnG,iBAAiB,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACnE,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,cAAc,EAAE,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;IAC/C,aAAa,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3D,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,2CAA2C;IAC3C,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACpD,gGAAgG;IAChG,eAAe,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACtD,QAAQ,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAA;IACrC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACzD,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IAC9E,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAC/D,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACtE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACvD,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAA;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,6FAA6F;IAC7F,kBAAkB,EAAE,MAAM,EAAE,CAAA;CAC7B,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,KAAG,MAAgC,CAAA;AAkCxE,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,EAAE,SAAS,MAAM,KAAG,MAkCrE,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MASrD,CAAA;AAcD,OAAO,EAAC,WAAW,EAAC,CAAA;AAEpB,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,cAAc,MAAM;;;;;;;CAOtE,CAAA;AAEF,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,EACf,cAAc,MAAM,EACpB,QAAO,MAAM,GAAG,SAAkB,KACjC,MAAM,EAER,CAAA;AAWD,eAAO,MAAM,QAAQ,GAAU,SAAS,YAAY,EAAE,MAAM,SAAS,KAAG,OAAO,CAAC,WAAW,CAmd1F,CAAA;AAmED,eAAO,MAAM,kBAAkB,GAAI,QAAQ,WAAW,KAAG,MAIxD,CAAA;AAID,eAAO,MAAM,UAAU,GAAU,MAAM,MAAM,EAAE,kBAAkC,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAY7G,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,KAAG,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAM5F,CAAA;AAEJ,eAAO,MAAM,eAAe;;;;;;uBAMP,MAAM;;;;;;;;gBAdM,MAAM,QAAQ,MAAM,EAAE,KAAG,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;6BAoBhE,MAAM;iCAUF,MAAM,WAAW,MAAM;uBAYvC,MAAM,MAAM,MAAM;CACrC,CAAA;AAIF,eAAO,MAAM,UAAU,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAQjE,CAAA;AAGD,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAyB1D,CAAA;AAEJ,eAAO,MAAM,YAAY,GAAU,OAAO,cAAc,EAAE,KAAG,OAAO,CAAC,MAAM,CAO1E,CAAA;AAED,eAAO,MAAM,UAAU,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CACd,CAAA;AAErD,eAAO,MAAM,SAAS,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CACd,CAAA"}
1
+ {"version":3,"file":"setupCommand.d.ts","sourceRoot":"","sources":["../src/setupCommand.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ import { agentSpec, cliHome, detectAgents, installAgentSkills, mcpServerEntry, m
8
8
  import { cliVersion, mcpConfig, validatedBaseUrl } from './setup.js';
9
9
  // The steps `--only` and `--skip` name, in the order setup runs them. `agents`
10
10
  // is not one: the mcp and skills steps need it.
11
- export const SETUP_STEPS = ['login', 'workspace', 'mcp', 'skills', 'app-env', 'hook', 'doctor'];
11
+ export const SETUP_STEPS = ['login', 'workspace', 'mcp', 'skills', 'auto-update', 'app-env', 'hook', 'doctor'];
12
12
  export const redactKey = (key) => `ah_…${key.slice(-4)}`;
13
13
  const scrub = (text, key) => key ? text.split(key).join(redactKey(key)) : text;
14
14
  const TOML_HEADER = /^\s*\[\[?\s*([^\]]+?)\s*\]\]?\s*(#.*)?$/;
@@ -441,6 +441,15 @@ export const runSetup = async (options, deps) => {
441
441
  steps.push({ name: 'skills', status: 'fail', detail: error instanceof Error ? error.message : String(error) });
442
442
  }
443
443
  }
444
+ // 4b. Auto-update: on by default; shown so nobody is surprised by it.
445
+ if (!skipped('auto-update')) {
446
+ try {
447
+ steps.push(await autoUpdateStep(options, deps));
448
+ }
449
+ catch (error) {
450
+ steps.push({ name: 'auto-update', status: 'fail', detail: error instanceof Error ? error.message : String(error) });
451
+ }
452
+ }
444
453
  // 5. Make the key visible to apps started outside a shell (macOS).
445
454
  const appEnv = skipped('app-env') ? undefined : await ensureAppEnv(options, deps, key);
446
455
  if (appEnv)
@@ -567,6 +576,26 @@ export const runSetup = async (options, deps) => {
567
576
  ...(exportLine ? { exportLine } : {}),
568
577
  };
569
578
  };
579
+ const autoUpdateStep = async (options, deps) => {
580
+ const name = 'auto-update';
581
+ const saved = await deps.readAutoUpdate();
582
+ const wanted = options.autoUpdate ?? saved ?? true;
583
+ const changes = options.autoUpdate !== undefined && options.autoUpdate !== (saved ?? true);
584
+ const onText = 'on: once a day a newer CLI installs itself in the background, keeping your login (turn off: `assethub setup --no-auto-update`)';
585
+ const offText = 'off: run `assethub update` yourself (turn on: `assethub setup --auto-update`)';
586
+ if (changes && options.dryRun)
587
+ return { name, status: 'skip', detail: `would turn auto-update ${wanted ? 'on' : 'off'} (dry run)`, change: 'would-update' };
588
+ if (options.autoUpdate !== undefined && options.autoUpdate !== saved)
589
+ await deps.writeAutoUpdate(options.autoUpdate);
590
+ const change = changes ? 'updated' : 'unchanged';
591
+ if (!wanted)
592
+ return { name, status: 'ok', detail: offText, change };
593
+ if (!(await deps.canSelfUpdate()))
594
+ return { name, status: 'skip', detail: 'this CLI is not a global install (npx or a source checkout), so it cannot update itself', change };
595
+ if (deps.autoUpdateBlockedByEnv)
596
+ return { name, status: 'skip', detail: 'on, but ASSETHUB_NO_AUTO_UPDATE, ASSETHUB_NO_UPDATE_CHECK or CI turns it off in this environment', change };
597
+ return { name, status: 'ok', detail: onText, change };
598
+ };
570
599
  const skillsStep = (report, dryRun) => {
571
600
  const changedLinks = report.links.filter(link => link.status === 'linked' || link.status === 'relinked');
572
601
  const keptLinks = report.links.filter(link => link.status === 'kept-existing');
@@ -575,7 +604,9 @@ const skillsStep = (report, dryRun) => {
575
604
  const parts = [
576
605
  report.status === 'kept-existing'
577
606
  ? `kept ${report.path} (not created by AssetHub)`
578
- : `${dryRun && copyChanged ? `would ${report.status === 'installed' ? 'install' : 'update'}` : report.status} ${report.path}${report.version ? ` (${report.version})` : ''}`,
607
+ : report.status === 'kept-newer'
608
+ ? `kept ${report.path} (${report.version}, newer than this CLI)`
609
+ : `${dryRun && copyChanged ? `would ${report.status === 'installed' ? 'install' : 'update'}` : report.status} ${report.path}${report.version ? ` (${report.version})` : ''}`,
579
610
  ...(changedLinks.length > 0
580
611
  ? [`${dryRun ? 'would link' : 'linked'} into ${listJoin(changedLinks.map(link => agentSpec(link.agent).name))}`]
581
612
  : []),
@@ -0,0 +1,71 @@
1
+ import { type UpdateDeps } from './update.js';
2
+ export declare const CHECK_INTERVAL_MS: number;
3
+ export declare const REFRESH_CHILD_ENV = "ASSETHUB_UPDATE_REFRESH_CHILD";
4
+ /** What the last background install did; announced once, then marked. */
5
+ export type AutoUpdateResult = {
6
+ from: string;
7
+ to: string;
8
+ at: string;
9
+ announced?: boolean;
10
+ } | {
11
+ from: string;
12
+ error: string;
13
+ at: string;
14
+ announced?: boolean;
15
+ };
16
+ export type UpdateCheckCache = {
17
+ checkedAt: string;
18
+ latest: string;
19
+ autoUpdate?: AutoUpdateResult;
20
+ /** When the `is available` line was last shown: at most once a day. */
21
+ noticedAt?: string;
22
+ };
23
+ export declare const updateCheckPath: (configPath: string) => string;
24
+ export declare const readUpdateCheck: (path: string) => Promise<UpdateCheckCache | undefined>;
25
+ export declare const writeUpdateCheck: (path: string, latest: string, now?: Date, autoUpdate?: AutoUpdateResult) => Promise<void>;
26
+ /** Rewrites the cache with some fields changed (announcement bookkeeping by the foreground command). */
27
+ export declare const patchUpdateCheck: (path: string, cache: UpdateCheckCache, patch: Partial<UpdateCheckCache>) => Promise<void>;
28
+ export declare const ATTEMPT_INTERVAL_MS: number;
29
+ export declare const attemptPath: (cachePath: string) => string;
30
+ /** Whether a refresh was started recently enough not to start another; records this attempt when not. */
31
+ export declare const claimRefreshAttempt: (cachePath: string, now?: Date) => Promise<boolean>;
32
+ export declare const settingsPath: (configPath: string) => string;
33
+ /** The saved choice: true or false, or undefined when never set (which means on). */
34
+ export declare const readAutoUpdateSetting: (path: string) => Promise<boolean | undefined>;
35
+ export declare const writeAutoUpdateSetting: (path: string, autoUpdate: boolean) => Promise<void>;
36
+ /** Auto-update is on by default; the saved setting or ASSETHUB_NO_AUTO_UPDATE turns it off. */
37
+ export declare const autoUpdateEnabled: (setting: boolean | undefined, env: NodeJS.ProcessEnv) => boolean;
38
+ /**
39
+ * Only a global install can be upgraded by `assethub update`: a project
40
+ * dependency, an npx run or a source checkout has nothing to announce. Same
41
+ * layouts as update.ts's detectInstall, without running `npm root --global`
42
+ * on every command.
43
+ */
44
+ export declare const isInstalledPackage: (cliPath: string) => boolean;
45
+ /** Opted out, or running somewhere nobody reads a hint (CI). */
46
+ export declare const updateCheckDisabled: (env: NodeJS.ProcessEnv) => boolean;
47
+ export type NoticeDecision = {
48
+ notices: string[];
49
+ refresh: boolean;
50
+ markAnnounced: boolean;
51
+ markNoticed: boolean;
52
+ };
53
+ /** What to print now, whether to mark a background install as announced, and whether to refresh. */
54
+ export declare const decideUpdateNotice: (current: string, cache: UpdateCheckCache | undefined, now: Date) => NoticeDecision;
55
+ /** One install at a time, background or `assethub update`: a second one leaves it alone. */
56
+ export declare const takeLock: (path: string) => Promise<boolean>;
57
+ /**
58
+ * Reads the latest version and, when auto-update is on and it is newer,
59
+ * installs it through `runUpdate` (same installer, same login check as
60
+ * `assethub update --yes`). Records the outcome for the next command to announce.
61
+ */
62
+ export declare const refreshUpdateCheck: (input: {
63
+ cachePath: string;
64
+ lockPath: string;
65
+ autoUpdate: boolean;
66
+ deps: UpdateDeps;
67
+ now?: () => Date;
68
+ }) => Promise<UpdateCheckCache | undefined>;
69
+ /** Starts the background refresh and returns at once; the child outlives this process. */
70
+ export declare const refreshInBackground: (node: string, cli: string, configPath: string) => void;
71
+ //# sourceMappingURL=updateNotice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateNotice.d.ts","sourceRoot":"","sources":["../src/updateNotice.ts"],"names":[],"mappings":"AASA,OAAO,EAA2C,KAAK,UAAU,EAAC,MAAM,aAAa,CAAA;AAErF,eAAO,MAAM,iBAAiB,QAAsB,CAAA;AACpD,eAAO,MAAM,iBAAiB,kCAAkC,CAAA;AAEhE,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GACxB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAC,GAC3D;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAC,CAAA;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,YAAY,MAAM,KAAG,MAAwD,CAAA;AAE7G,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAcxF,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,EACZ,QAAQ,MAAM,EACd,UAAgB,EAChB,aAAa,gBAAgB,KAC5B,OAAO,CAAC,IAAI,CAId,CAAA;AAED,wGAAwG;AACxG,eAAO,MAAM,gBAAgB,GAAU,MAAM,MAAM,EAAE,OAAO,gBAAgB,EAAE,OAAO,OAAO,CAAC,gBAAgB,CAAC,KAAG,OAAO,CAAC,IAAI,CAE5H,CAAA;AAID,eAAO,MAAM,mBAAmB,QAAiB,CAAA;AACjD,eAAO,MAAM,WAAW,GAAI,WAAW,MAAM,KAAG,MAAgC,CAAA;AAEhF,yGAAyG;AACzG,eAAO,MAAM,mBAAmB,GAAU,WAAW,MAAM,EAAE,UAAgB,KAAG,OAAO,CAAC,OAAO,CAO9F,CAAA;AAID,eAAO,MAAM,YAAY,GAAI,YAAY,MAAM,KAAG,MAAoD,CAAA;AAEtG,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAOrF,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAU,MAAM,MAAM,EAAE,YAAY,OAAO,KAAG,OAAO,CAAC,IAAI,CAU5F,CAAA;AAID,+FAA+F;AAC/F,eAAO,MAAM,iBAAiB,GAAI,SAAS,OAAO,GAAG,SAAS,EAAE,KAAK,MAAM,CAAC,UAAU,KAAG,OACD,CAAA;AAExF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,OAWpD,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,CAAC,UAAU,KAAG,OACJ,CAAA;AAEzD,MAAM,MAAM,cAAc,GAAG;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAC,CAAA;AAIhH,oGAAoG;AACpG,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,EACf,OAAO,gBAAgB,GAAG,SAAS,EACnC,KAAK,IAAI,KACR,cAiBF,CAAA;AAMD,4FAA4F;AAC5F,eAAO,MAAM,QAAQ,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,CAgB5D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAU,OAAO;IAC9C,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,UAAU,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB,KAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAmBvC,CAAA;AAED,0FAA0F;AAC1F,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,EAAE,KAAK,MAAM,EAAE,YAAY,MAAM,KAAG,IAcnF,CAAA"}
@@ -0,0 +1,189 @@
1
+ // Keeping the CLI current. Once a day a detached `assethub update --refresh-check`
2
+ // process reads the latest version and, with auto-update on (the default),
3
+ // installs it the way `assethub update` would. No command ever waits on the
4
+ // network or the install: each one only reads the small cache file the
5
+ // background process leaves behind, announces what happened once, and moves on.
6
+ import { spawn } from 'node:child_process';
7
+ import { mkdir, open, readFile, rm, stat, writeFile } from 'node:fs/promises';
8
+ import { dirname, join } from 'node:path';
9
+ import { compareVersions, PACKAGE_NAME, runUpdate } from './update.js';
10
+ export const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
11
+ export const REFRESH_CHILD_ENV = 'ASSETHUB_UPDATE_REFRESH_CHILD';
12
+ export const updateCheckPath = (configPath) => join(dirname(configPath), 'update-check.json');
13
+ export const readUpdateCheck = async (path) => {
14
+ try {
15
+ const cache = JSON.parse(await readFile(path, 'utf8'));
16
+ return typeof cache.checkedAt === 'string' && typeof cache.latest === 'string'
17
+ ? {
18
+ checkedAt: cache.checkedAt,
19
+ latest: cache.latest,
20
+ ...(cache.autoUpdate && typeof cache.autoUpdate === 'object' ? { autoUpdate: cache.autoUpdate } : {}),
21
+ ...(typeof cache.noticedAt === 'string' ? { noticedAt: cache.noticedAt } : {}),
22
+ }
23
+ : undefined;
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ };
29
+ export const writeUpdateCheck = async (path, latest, now = new Date(), autoUpdate) => {
30
+ await mkdir(dirname(path), { recursive: true });
31
+ const cache = { checkedAt: now.toISOString(), latest, ...(autoUpdate ? { autoUpdate } : {}) };
32
+ await writeFile(path, `${JSON.stringify(cache)}\n`);
33
+ };
34
+ /** Rewrites the cache with some fields changed (announcement bookkeeping by the foreground command). */
35
+ export const patchUpdateCheck = async (path, cache, patch) => {
36
+ await writeFile(path, `${JSON.stringify({ ...cache, ...patch })}\n`);
37
+ };
38
+ // A failed or slow refresh leaves the cache stale; this marker keeps every
39
+ // command from starting another one. Retried at most once an hour.
40
+ export const ATTEMPT_INTERVAL_MS = 60 * 60 * 1000;
41
+ export const attemptPath = (cachePath) => `${cachePath}.attempt`;
42
+ /** Whether a refresh was started recently enough not to start another; records this attempt when not. */
43
+ export const claimRefreshAttempt = async (cachePath, now = new Date()) => {
44
+ const path = attemptPath(cachePath);
45
+ const last = await readFile(path, 'utf8').then(text => Date.parse(text.trim()), () => Number.NaN);
46
+ if (Number.isFinite(last) && now.getTime() - last < ATTEMPT_INTERVAL_MS && last <= now.getTime())
47
+ return false;
48
+ await mkdir(dirname(path), { recursive: true });
49
+ await writeFile(path, `${now.toISOString()}\n`);
50
+ return true;
51
+ };
52
+ // ---- the auto-update setting (on unless turned off) ----
53
+ export const settingsPath = (configPath) => join(dirname(configPath), 'settings.json');
54
+ /** The saved choice: true or false, or undefined when never set (which means on). */
55
+ export const readAutoUpdateSetting = async (path) => {
56
+ try {
57
+ const value = JSON.parse(await readFile(path, 'utf8')).autoUpdate;
58
+ return typeof value === 'boolean' ? value : undefined;
59
+ }
60
+ catch {
61
+ return undefined;
62
+ }
63
+ };
64
+ export const writeAutoUpdateSetting = async (path, autoUpdate) => {
65
+ let settings = {};
66
+ try {
67
+ const parsed = JSON.parse(await readFile(path, 'utf8'));
68
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
69
+ settings = parsed;
70
+ }
71
+ catch {
72
+ // missing or unreadable: start fresh
73
+ }
74
+ await mkdir(dirname(path), { recursive: true });
75
+ await writeFile(path, `${JSON.stringify({ ...settings, autoUpdate }, null, 2)}\n`);
76
+ };
77
+ const truthy = (value) => ['1', 'true', 'yes'].includes((value ?? '').toLowerCase());
78
+ /** Auto-update is on by default; the saved setting or ASSETHUB_NO_AUTO_UPDATE turns it off. */
79
+ export const autoUpdateEnabled = (setting, env) => setting !== false && !truthy(env.ASSETHUB_NO_AUTO_UPDATE) && !updateCheckDisabled(env);
80
+ /**
81
+ * Only a global install can be upgraded by `assethub update`: a project
82
+ * dependency, an npx run or a source checkout has nothing to announce. Same
83
+ * layouts as update.ts's detectInstall, without running `npm root --global`
84
+ * on every command.
85
+ */
86
+ export const isInstalledPackage = (cliPath) => {
87
+ const path = cliPath.replaceAll('\\', '/');
88
+ if (!path.includes(`/node_modules/${PACKAGE_NAME}/`) || path.includes('/_npx/'))
89
+ return false;
90
+ return [
91
+ `/lib/node_modules/${PACKAGE_NAME}/`, // npm, nvm, Homebrew node, a custom npm prefix
92
+ `/npm/node_modules/${PACKAGE_NAME}/`, // npm on Windows (%AppData%/npm)
93
+ '/.volta/',
94
+ '/.bun/install/global/',
95
+ '/pnpm/global/',
96
+ '/yarn/global/',
97
+ ].some(segment => path.includes(segment));
98
+ };
99
+ /** Opted out, or running somewhere nobody reads a hint (CI). */
100
+ export const updateCheckDisabled = (env) => truthy(env.ASSETHUB_NO_UPDATE_CHECK) || Boolean(env.CI);
101
+ const TURN_OFF = 'turn off with `assethub setup --no-auto-update`';
102
+ /** What to print now, whether to mark a background install as announced, and whether to refresh. */
103
+ export const decideUpdateNotice = (current, cache, now) => {
104
+ const checked = cache ? Date.parse(cache.checkedAt) : Number.NaN;
105
+ const refresh = !Number.isFinite(checked) || now.getTime() - checked >= CHECK_INTERVAL_MS || checked > now.getTime();
106
+ const notices = [];
107
+ const result = cache?.autoUpdate;
108
+ const announce = result !== undefined && !result.announced;
109
+ if (announce && 'to' in result && result.to === current)
110
+ notices.push(`${PACKAGE_NAME} updated itself from ${result.from} to ${result.to} (auto-update; ${TURN_OFF}).`);
111
+ if (announce && 'error' in result)
112
+ notices.push(`${PACKAGE_NAME} could not update itself to ${cache?.latest}: ${result.error}`);
113
+ const noticed = cache?.noticedAt ? Date.parse(cache.noticedAt) : Number.NaN;
114
+ const noticeDue = !Number.isFinite(noticed) || now.getTime() - noticed >= CHECK_INTERVAL_MS || noticed > now.getTime();
115
+ // A failed install is reported together with the release it could not install.
116
+ const available = cache !== undefined && compareVersions(cache.latest, current) > 0 && (noticeDue || (announce && 'error' in result));
117
+ if (available)
118
+ notices.push(`${PACKAGE_NAME} ${cache.latest} is available (you have ${current}). Run \`assethub update\` to upgrade; your login is kept.`);
119
+ return { notices, refresh, markAnnounced: announce, markNoticed: available };
120
+ };
121
+ // ---- the background refresh ----
122
+ const LOCK_STALE_MS = 15 * 60 * 1000;
123
+ /** One install at a time, background or `assethub update`: a second one leaves it alone. */
124
+ export const takeLock = async (path) => {
125
+ await mkdir(dirname(path), { recursive: true });
126
+ try {
127
+ await (await open(path, 'wx')).close();
128
+ return true;
129
+ }
130
+ catch {
131
+ const age = await stat(path).then(info => Date.now() - info.mtimeMs, () => 0);
132
+ if (age < LOCK_STALE_MS)
133
+ return false;
134
+ await rm(path, { force: true });
135
+ try {
136
+ await (await open(path, 'wx')).close();
137
+ return true;
138
+ }
139
+ catch {
140
+ return false;
141
+ }
142
+ }
143
+ };
144
+ /**
145
+ * Reads the latest version and, when auto-update is on and it is newer,
146
+ * installs it through `runUpdate` (same installer, same login check as
147
+ * `assethub update --yes`). Records the outcome for the next command to announce.
148
+ */
149
+ export const refreshUpdateCheck = async (input) => {
150
+ const now = input.now ?? (() => new Date());
151
+ if (!(await takeLock(input.lockPath)))
152
+ return undefined;
153
+ try {
154
+ const [current, latest] = await Promise.all([input.deps.currentVersion(), input.deps.latestVersion()]);
155
+ let autoUpdate;
156
+ if (input.autoUpdate && compareVersions(latest, current) > 0) {
157
+ try {
158
+ const report = await runUpdate({ yes: true }, { ...input.deps, latestVersion: async () => latest, interactive: false });
159
+ if (report.status === 'updated')
160
+ autoUpdate = { from: report.from, to: report.to, at: now().toISOString() };
161
+ }
162
+ catch (error) {
163
+ autoUpdate = { from: current, error: error instanceof Error ? error.message : String(error), at: now().toISOString() };
164
+ }
165
+ }
166
+ await writeUpdateCheck(input.cachePath, latest, now(), autoUpdate);
167
+ return { checkedAt: now().toISOString(), latest, ...(autoUpdate ? { autoUpdate } : {}) };
168
+ }
169
+ finally {
170
+ await rm(input.lockPath, { force: true });
171
+ }
172
+ };
173
+ /** Starts the background refresh and returns at once; the child outlives this process. */
174
+ export const refreshInBackground = (node, cli, configPath) => {
175
+ try {
176
+ const child = spawn(node, [cli, 'update', '--refresh-check', '--config', configPath], {
177
+ detached: true,
178
+ stdio: 'ignore',
179
+ // Marks the child (and the `assethub doctor` it runs to verify an install) so it
180
+ // neither prints notices nor starts another refresh.
181
+ env: { ...process.env, [REFRESH_CHILD_ENV]: '1' },
182
+ });
183
+ child.on('error', () => { });
184
+ child.unref();
185
+ }
186
+ catch {
187
+ // best effort: a missing hint must never fail the user's command
188
+ }
189
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assethub/cli",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "description": "Command line interface for AssetHub.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,6 +31,23 @@ description: |
31
31
 
32
32
  Always in this order. Skipping a step is how credits get wasted.
33
33
 
34
+ ### 0. Make sure the CLI is current (once per session)
35
+
36
+ ```bash
37
+ assethub update --check
38
+ ```
39
+
40
+ Operations, models and these rules change between releases, and an old CLI can reject flags the server now expects or miss a safety fix. Run this once at the start of a session, before the first paid call. It is free.
41
+
42
+ Auto-update is on by default: once a day the CLI installs a newer release by itself in the background, and the next command says so on stderr (`updated itself from … to …`). So this check usually reports `up_to_date`. It is still needed: auto-update may be off (`assethub setup --no-auto-update`, `ASSETHUB_NO_AUTO_UPDATE=1`, CI), may have failed, or may not have run yet today.
43
+
44
+ - `"status": "up_to_date"`: carry on.
45
+ - `"status": "update_available"`: run `assethub update --yes`. It upgrades the global install with the package manager that installed it and keeps the saved login. Tell the user which version you moved from and to (`from`, `to`). The next `assethub` command refreshes this skill automatically, so re-read it after that command; its rules may have changed.
46
+ - `update` refuses (an `npx` or project-local install, or a source checkout) or fails (no network): continue on the current version and tell the user once. Do not retry it in a loop.
47
+ - Do not update while another `assethub` command is still running (a `--wait` in another terminal). Runs on the server are not affected by an update; resume them afterwards with their `runId`.
48
+
49
+ The CLI also prints a one-line `is available` or `could not update itself` notice on stderr. Treat it the same way. When you see `updated itself`, re-read this skill after that command, as above. Never turn auto-update off for the user.
50
+
34
51
  ### 1. Check what this key can do
35
52
 
36
53
  ```bash