@helix3/helix-cli 0.1.14-helix3.219 → 0.1.14-helix3.221

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
@@ -41,7 +41,7 @@ The token is stored in `~/.helix/credentials.json` (mode `600`). It's sent as
41
41
  | `helix logout` | Remove the stored token. |
42
42
  | `helix whoami` | Show current login status. |
43
43
  | `helix init <dir> [--kind character\|multiplayer\|scene]` | Scaffold a complete, build-ready Vite + TypeScript world. `--kind scene` emits a multiplayer world whose ENVIRONMENT is a sealed HELIX Scene v2 document — the starter document is compiled offline into `public/scene/mall/`, pinned in `public/helix.json`'s `scene` field, kept out of the bundle by `public/.helixignore`, and lit by `public/helix.visuals.json` through the `scene` + `visual` system pins. |
44
- | `helix install [--update]` | Resolve a world's `systems`/`abilities` pins (a v0.2 **or v0.3** manifest) → materialize the modules, the `three` import map, and `helix.runtime.ts`. |
44
+ | `helix install [--update] [--asset-pack <slug@range>]` | Resolve a world's `systems`/`abilities` pins (a v0.2 **or v0.3** manifest) → materialize the modules, the `three` import map, and `helix.runtime.ts`. Repeat `--asset-pack` to constrain specific system assets for this resolution. |
45
45
  | `helix validate [dir]` | Validate a bundle locally — the exact rules the server enforces. |
46
46
  | `helix publish [dir] [--thumbnail <file>] [--upload-source] [--source-dir <path>]` | Validate → resolve/create the world → upload files → finalize → set the cover image → print the play URL. `--upload-source` additionally sends the project's source through a separate private channel so the world can be edited on the website later — **off by default**. |
47
47
  | `helix list` | List your worlds. |
@@ -73,6 +73,36 @@ The token is stored in `~/.helix/credentials.json` (mode `600`). It's sent as
73
73
  | `helix unreal publish <candidate> --world <slug>` | Upload a **cooked** Unreal world to HELIX: verify the candidate, presign, upload every artifact, finalize, and promote it as the world's active build. |
74
74
  | `helix doctor [--project <dir>]` | Print the environment + whether the @helix toolchain (CLI/MCP/SDK/manifest) is current. |
75
75
 
76
+ ### Constraining system assets during installation
77
+
78
+ To update system code while deliberately selecting a compatible asset version:
79
+
80
+ ```bash
81
+ # With an exact system pin in helix.json, e.g. humanoid-character: "0.2.65"
82
+ helix install --update --asset-pack humanoid-character-assets@0.1.4
83
+ ```
84
+
85
+ `--asset-pack <slug@range>` is repeatable for different asset slugs. Supported ranges
86
+ are exact versions, `^`/`~` ranges (partial versions are padded as for system pins),
87
+ `latest`, and `*`. Resolution selects the highest active version satisfying both
88
+ the explicit constraint and the system envelope's asset requirement. Complete exact
89
+ pins use the authoritative public version-detail endpoint, including active staged
90
+ versions. Incorrect kinds/identities, unused slugs, duplicates, invalid ranges,
91
+ missing versions, and incompatible constraints fail before installer writes.
92
+
93
+ These constraints apply to **this invocation**. An existing lock requires `--update`;
94
+ a fresh install can use the option without a lock. The installer resolves from the
95
+ catalog and writes the exact asset URL/version and unchanged envelope `range` into
96
+ the lock, with invocation intent recorded in top-level `assetPackConstraints`.
97
+ It does not reuse unverified asset metadata
98
+ from an old lock. Normal `helix install` then replays that generated lock as usual.
99
+
100
+ A subsequent `helix install --update` without the option refreshes all dependencies
101
+ from their ordinary requirements, including previously constrained assets. Repeat the
102
+ option in your update command to retain the constraint. It is not a world manifest
103
+ field or a permanent asset freeze, and cannot be combined with local first-party
104
+ installation. Generated locks, import maps and runtime modules remain installer-owned.
105
+
76
106
  ## Unreal runtime projects
77
107
 
78
108
  `helix unreal init <project> --mode shared-runtime|native-game` creates `helix.unreal.json`.
package/dist/index.js CHANGED
@@ -671,6 +671,7 @@ program
671
671
  .description("Resolve this v0.2 world's system/ability pins, download their code, and write installed.json + helix.lock.json")
672
672
  .argument('[path]', 'a local ability bundle to copy in (requires --first-party); omit for the world resolve+install')
673
673
  .option('--update', 're-resolve pins against the live catalog and rewrite the lock (else the existing lock is replayed)')
674
+ .option('--asset-pack <slug@range>', 'constrain a system asset-pack for this resolution (repeatable; existing lock requires --update)', (value, previous) => [...previous, value], [])
674
675
  .option('--first-party', 'FIRST-PARTY only: copy the local <path> bundle (code AND clips) into helix_modules — for engine devs testing co-located assets')
675
676
  .option('--local', 'alias of --first-party (deprecated)')
676
677
  .option('--api-url <url>', 'platform API base URL (default: the environment you are logged in to)')
@@ -684,6 +685,8 @@ program
684
685
  opts.apiUrl = opts.apiUrl ?? (0, config_1.loadCredentials)()?.apiUrl ?? config_1.DEFAULT_API_URL;
685
686
  const firstParty = Boolean(opts.firstParty || opts.local);
686
687
  if (path !== undefined || firstParty) {
688
+ if (opts.assetPack?.length)
689
+ fail('✖ --asset-pack is only supported for world pin resolution');
687
690
  if (!path) {
688
691
  fail('✖ --first-party needs a bundle path: helix install <bundle> --first-party');
689
692
  }
@@ -704,9 +707,20 @@ program
704
707
  return;
705
708
  }
706
709
  try {
710
+ const assetPacks = Object.create(null);
711
+ for (const value of opts.assetPack ?? []) {
712
+ const at = value.indexOf('@');
713
+ if (at <= 0 || at === value.length - 1)
714
+ throw new Error('--asset-pack requires <slug@range>');
715
+ const slug = value.slice(0, at);
716
+ if (Object.hasOwn(assetPacks, slug))
717
+ throw new Error(`duplicate asset-pack constraint "${slug}"`);
718
+ assetPacks[slug] = value.slice(at + 1);
719
+ }
707
720
  const result = await (0, lib_1.installWorld)(process.cwd(), {
708
721
  apiUrl: opts.apiUrl,
709
722
  update: opts.update,
723
+ assetPacks,
710
724
  onProgress: (msg) => console.log(msg),
711
725
  });
712
726
  const sys = result.systems.map((s) => `${s.slug}@${s.version}`).join(', ') || '(none)';