@helix3/helix-cli 0.1.18-helix3.276 → 0.1.18-helix3.278

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.
@@ -0,0 +1,47 @@
1
+ import type { CliCredentials } from './config';
2
+ export type PackagePin = {
3
+ packageId: string;
4
+ version: string;
5
+ manifestCid: string;
6
+ };
7
+ export type PublishAddOnPackageVersionOptions = {
8
+ reason: string;
9
+ notes?: string;
10
+ expectedCurrentVersionId?: string;
11
+ updateId?: string;
12
+ /** Bounded wait for the uploaded mesh to clear verify/optimize and reach `ready`. */
13
+ assetReadyTimeoutMs?: number;
14
+ onProgress?: (message: string) => void;
15
+ };
16
+ export type PublishAddOnPackageVersionResult = {
17
+ itemId: string;
18
+ revision: number;
19
+ packagePin: PackagePin;
20
+ previousRevision: number;
21
+ previousPackagePin: PackagePin;
22
+ updateId?: string;
23
+ [key: string]: unknown;
24
+ };
25
+ /**
26
+ * Ship a corrected mesh onto an ALREADY-PUBLISHED character add-on, in place:
27
+ * same item id, same owner, same serial, same listings and distributions —
28
+ * every existing owner picks up the new geometry at their next spawn/session
29
+ * start. Two backend steps, in sequence:
30
+ *
31
+ * 1. The ordinary asset door: `POST :id/assets` (presign) → PUT the bytes →
32
+ * `POST :id/assets/:assetId/finalize` — the exact verify/optimize
33
+ * pipeline `helix item publish` runs on a brand-new item. This function
34
+ * polls the asset's status afterward, because finalize only STARTS that
35
+ * pipeline; it does not wait for it.
36
+ * 2. `POST :id/add-on-package/versions` — seals that now-Ready GLB as a new
37
+ * Version of the item's own Continuum Package and moves the item's
38
+ * pointer through the platform's real, audited, revision-checked
39
+ * version-update path (the same one Skylark/Ocean Drive republish
40
+ * through). Refused (409) if the new geometry breaks the compatibility
41
+ * contract (slot removed, contract changed, …) — that is not a bug in
42
+ * this command, it means the change is not a valid update to this item.
43
+ *
44
+ * There is no local dry-run: everything here is a server-verified round trip,
45
+ * same as `helix item publish`.
46
+ */
47
+ export declare function publishAddOnPackageVersion(itemId: string, file: string, options: PublishAddOnPackageVersionOptions, credentials: CliCredentials): Promise<PublishAddOnPackageVersionResult>;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.publishAddOnPackageVersion = publishAddOnPackageVersion;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_path_1 = require("node:path");
6
+ const api_1 = require("./api");
7
+ const item_1 = require("./item");
8
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9
+ const MAX_ADDON_WEB_GLB_BYTES = 64 * 1024 * 1024; // mirrors the backend's own ceiling for this lane
10
+ const DEFAULT_ASSET_READY_TIMEOUT_MS = 180_000;
11
+ const ASSET_POLL_INTERVAL_MS = 2_000;
12
+ /**
13
+ * Ship a corrected mesh onto an ALREADY-PUBLISHED character add-on, in place:
14
+ * same item id, same owner, same serial, same listings and distributions —
15
+ * every existing owner picks up the new geometry at their next spawn/session
16
+ * start. Two backend steps, in sequence:
17
+ *
18
+ * 1. The ordinary asset door: `POST :id/assets` (presign) → PUT the bytes →
19
+ * `POST :id/assets/:assetId/finalize` — the exact verify/optimize
20
+ * pipeline `helix item publish` runs on a brand-new item. This function
21
+ * polls the asset's status afterward, because finalize only STARTS that
22
+ * pipeline; it does not wait for it.
23
+ * 2. `POST :id/add-on-package/versions` — seals that now-Ready GLB as a new
24
+ * Version of the item's own Continuum Package and moves the item's
25
+ * pointer through the platform's real, audited, revision-checked
26
+ * version-update path (the same one Skylark/Ocean Drive republish
27
+ * through). Refused (409) if the new geometry breaks the compatibility
28
+ * contract (slot removed, contract changed, …) — that is not a bug in
29
+ * this command, it means the change is not a valid update to this item.
30
+ *
31
+ * There is no local dry-run: everything here is a server-verified round trip,
32
+ * same as `helix item publish`.
33
+ */
34
+ async function publishAddOnPackageVersion(itemId, file, options, credentials) {
35
+ if (!UUID_RE.test(itemId))
36
+ throw new Error('Item ID must be a UUID.');
37
+ const reason = options.reason?.trim() ?? '';
38
+ if (!reason || reason.length > 500) {
39
+ throw new Error('--reason is required (1-500 characters) and lands in the audit ledger.');
40
+ }
41
+ const bytes = await (0, promises_1.readFile)(file);
42
+ (0, item_1.assertLooksLikeGlb)(bytes, file);
43
+ if (bytes.length > MAX_ADDON_WEB_GLB_BYTES) {
44
+ throw new Error(`"${file}" is ${(bytes.length / (1024 * 1024)).toFixed(1)} MiB; an add-on's web GLB must be at most ${MAX_ADDON_WEB_GLB_BYTES / (1024 * 1024)} MiB.`);
45
+ }
46
+ const progress = options.onProgress ?? (() => { });
47
+ const api = api_1.HelixApi.forCredentials(credentials);
48
+ const filename = (0, node_path_1.basename)(file);
49
+ const mimeType = 'model/gltf-binary';
50
+ progress('Requesting a presigned upload for the corrected mesh…');
51
+ const presign = await api.requestItem('POST', `/api/v1/universal-items/${itemId}/assets`, {
52
+ rendition: 'web',
53
+ filename,
54
+ mimeType,
55
+ sizeBytes: bytes.length,
56
+ });
57
+ if (!presign.uploadUrl)
58
+ throw new Error('Server did not return an upload URL for the web asset.');
59
+ progress('Uploading the mesh…');
60
+ await (0, api_1.uploadToTicket)(presign.uploadUrl, Buffer.from(bytes), mimeType);
61
+ progress('Finalizing the upload (verify → optimize → thumbnail)…');
62
+ await api.requestItem('POST', `/api/v1/universal-items/${itemId}/assets/${presign.id}/finalize`, { sizeBytes: bytes.length });
63
+ progress('Waiting for the pipeline to mark the mesh ready…');
64
+ const ready = await waitForAssetReady(api, itemId, presign.id, options.assetReadyTimeoutMs ?? DEFAULT_ASSET_READY_TIMEOUT_MS);
65
+ if (ready.status !== 'ready') {
66
+ throw new Error(`Upload finished but the pipeline left it "${ready.status}", not "ready" — it will not be sealed into a new Package Version. Check the item's assets for the rejection reason.`);
67
+ }
68
+ progress('Sealing the new Package Version and publishing it to every owner…');
69
+ const body = { reason };
70
+ if (options.notes !== undefined)
71
+ body.notes = options.notes;
72
+ if (options.expectedCurrentVersionId !== undefined)
73
+ body.expectedCurrentVersionId = options.expectedCurrentVersionId;
74
+ if (options.updateId !== undefined)
75
+ body.updateId = options.updateId;
76
+ return api.request('POST', `/api/v1/universal-items/${itemId}/add-on-package/versions`, body);
77
+ }
78
+ async function waitForAssetReady(api, itemId, assetId, timeoutMs) {
79
+ const deadline = Date.now() + timeoutMs;
80
+ for (;;) {
81
+ const assets = await api.request('GET', `/api/v1/universal-items/${itemId}/assets`);
82
+ const row = assets.find((a) => a.id === assetId);
83
+ if (!row)
84
+ throw new Error(`Asset ${assetId} disappeared from ${itemId}'s asset list.`);
85
+ if (row.status === 'ready' || row.status === 'rejected' || row.status === 'failed')
86
+ return row;
87
+ if (Date.now() >= deadline) {
88
+ throw new Error(`Timed out after ${Math.round(timeoutMs / 1000)}s waiting for asset ${assetId} to leave "${row.status}".`);
89
+ }
90
+ await new Promise((resolve) => setTimeout(resolve, ASSET_POLL_INTERVAL_MS));
91
+ }
92
+ }
93
+ //# sourceMappingURL=addOnPackageVersion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addOnPackageVersion.js","sourceRoot":"","sources":["../src/addOnPackageVersion.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ const commands_1 = require("./sceneHlod/commands");
39
39
  const placementRequest_1 = require("./placementRequest");
40
40
  const bundle_1 = require("./bundle");
41
41
  const avatarMeshReplacement_1 = require("./avatarMeshReplacement");
42
+ const addOnPackageVersion_1 = require("./addOnPackageVersion");
42
43
  const node_child_process_1 = require("node:child_process");
43
44
  const node_fs_1 = require("node:fs");
44
45
  const node_path_1 = require("node:path");
@@ -1207,6 +1208,49 @@ item
1207
1208
  fail(error instanceof lib_1.ApiError ? error.render() : String(error));
1208
1209
  }
1209
1210
  });
1211
+ // Ship a fix to an already-published item WITHOUT churning it onto a new id:
1212
+ // same item, same owner, same serial, same listings/distributions, every
1213
+ // existing owner picks up the new geometry at their next spawn. Character
1214
+ // add-ons only today — the backend's `add-on-package/versions` lane exists
1215
+ // because an add-on's Continuum Package is built FROM the item's own uploaded
1216
+ // mesh, unlike a vehicle or Scene Package, which are authored and sealed
1217
+ // independently and update through the generic (not-yet-CLI-exposed)
1218
+ // `POST :itemId/versions`. `helix item delist` is the OTHER lane here and does
1219
+ // something different: it takes an item off browse/marketplace for good,
1220
+ // rather than shipping owners an update.
1221
+ item
1222
+ .command('publish-version')
1223
+ .description('Ship a corrected mesh onto an already-published character add-on, IN PLACE — new Package Version, same item id/owner/listings; every existing owner gets it next spawn')
1224
+ .argument('<item-id>', 'existing, already-published character add-on item UUID')
1225
+ .argument('<file>', 'corrected mesh (binary glTF, .glb)')
1226
+ .requiredOption('--reason <text>', 'required, lands in the audit ledger (e.g. "fixed poke-through on the left cuff")')
1227
+ .option('--notes <text>', 'optional creator-facing changelog note')
1228
+ .option('--expected-current-version-id <uuid>', 'refuse if the item has moved off this Package Version since you last read it')
1229
+ .option('--update-id <uuid>', 'idempotency key — retrying with the same id never double-publishes')
1230
+ .option('--json', 'machine-readable output')
1231
+ .action(async (itemId, file, opts) => {
1232
+ const creds = requireAuth();
1233
+ try {
1234
+ const result = await (0, addOnPackageVersion_1.publishAddOnPackageVersion)(itemId, (0, node_path_1.resolve)(file), {
1235
+ reason: opts.reason,
1236
+ notes: opts.notes,
1237
+ expectedCurrentVersionId: opts.expectedCurrentVersionId,
1238
+ updateId: opts.updateId,
1239
+ onProgress: (message) => !opts.json && console.log(message),
1240
+ }, creds);
1241
+ if (opts.json) {
1242
+ console.log(JSON.stringify(result, null, 2));
1243
+ return;
1244
+ }
1245
+ console.log(`✔ Published a new Version of ${itemId} — revision ${result.previousRevision} → ${result.revision}`);
1246
+ console.log(` package: ${result.packagePin.packageId}@${result.packagePin.version}`);
1247
+ console.log(` previous: ${result.previousPackagePin.packageId}@${result.previousPackagePin.version}`);
1248
+ console.log(' same item id, same owner(s); existing owners see the change at their next spawn/session start.');
1249
+ }
1250
+ catch (error) {
1251
+ fail(error instanceof lib_1.ApiError ? error.render() : `✖ ${error instanceof Error ? error.message : String(error)}`);
1252
+ }
1253
+ });
1210
1254
  // One command prints the whole Character-Creator vocabulary a wearable must
1211
1255
  // declare — the 34 slot tags AND the two genders. There is no `list-genders`:
1212
1256
  // a dedicated command for a closed two-value list is a command to discover, to
@@ -1214,14 +1258,14 @@ item
1214
1258
  // list it is always read with. `--gender`'s own help carries the same values.
1215
1259
  item
1216
1260
  .command('search')
1217
- .description("Search the published MARKETPLACE catalogue — including Looks (user-facing name for a `build`: a free, never-owned preset), the dressed presets someone already assembled. A Look is never owned itself; its parts are acquired individually from their own listings")
1261
+ .description("Search the published MARKETPLACE catalogue — including Presets (user-facing name for a `build`: a free, never-owned preset), the dressed presets someone already assembled. A Preset is never owned itself; its parts are acquired individually from their own listings")
1218
1262
  .argument('[query]', 'free text over title, slug and tags')
1219
1263
  .option('--kind <kind>', 'universal item kind (avatar, wearable, vehicle, weapon, add_on, home_item, emote, prop)')
1220
1264
  .option('--category <category>', 'marketplace category')
1221
1265
  .option('--sub-category <subCategory>', 'form factor beneath the category, e.g. car / rifle / art')
1222
1266
  .option('--tag <tag>', 'exact tag')
1223
1267
  .option('--host-kind <hostKind>', 'host class, e.g. vehicle / character / weapon')
1224
- .option('--builds <mode>', `Looks (API identifier: builds) in the results: ${itemSearch_1.BUILD_FILTER_MODES.join(' | ')} (default include — a Look is filed under its base's own category, so it comes back alongside bases unless you narrow)`)
1268
+ .option('--builds <mode>', `Presets (API identifier: builds) in the results: ${itemSearch_1.BUILD_FILTER_MODES.join(' | ')} (default include — a Preset is filed under its base's own category, so it comes back alongside bases unless you narrow)`)
1225
1269
  .option('--limit <n>', 'rows to return (1-100, default 20)')
1226
1270
  .option('--json', 'machine-readable output')
1227
1271
  .action(async (query, opts) => {
@@ -1247,10 +1291,10 @@ item
1247
1291
  return;
1248
1292
  }
1249
1293
  for (const row of result.items) {
1250
- // The Look mark leads the line (`buildKind` is the field's API name;
1251
- // "Look" is what a human reads). A Look is filed under its base's
1294
+ // The Preset mark leads the line (`buildKind` is the field's API name;
1295
+ // "Preset" is what a human reads). A Preset is filed under its base's
1252
1296
  // own kind, so without it these rows are indistinguishable from the
1253
- // bases they were assembled from — which is the whole reason Looks
1297
+ // bases they were assembled from — which is the whole reason Presets
1254
1298
  // do not get a category of their own.
1255
1299
  const mark = row.buildKind ? `[look:${row.buildKind}] ` : '';
1256
1300
  const price = row.priceLix === null ? 'unlisted' : `Ⱡ${row.priceLix}`;
@@ -1267,7 +1311,7 @@ item
1267
1311
  });
1268
1312
  item
1269
1313
  .command('build')
1270
- .description("What is in a published Look (API identifier: build): its base, its add-on parts, what you own, and what the parts cost together. A Look is never owned itself — only its parts are, each bought or held from its own listing")
1314
+ .description("What is in a published Preset (API identifier: build): its base, its add-on parts, what you own, and what the parts cost together. A Preset is never owned itself — only its parts are, each bought or held from its own listing")
1271
1315
  .argument('<itemId>', 'universal item id of the build')
1272
1316
  .option('--json', 'machine-readable output')
1273
1317
  .action(async (itemId, opts) => {
@@ -2179,16 +2223,16 @@ product
2179
2223
  });
2180
2224
  // --- helix vault -------------------------------------------------------------
2181
2225
  // The Continuum Vault catalog (`GET /api/v1/vault/catalog`) — the table that
2182
- // backs the website's Vault page and actually lists Looks (build Packages),
2226
+ // backs the website's Vault page and actually lists Presets (build Packages),
2183
2227
  // characters, vehicles, weapons, devices, and every other sealed Continuum
2184
2228
  // Package. Distinct from `helix assets search`, which hits the LEGACY
2185
- // `/api/v1/vault/assets` table and has no Looks and never will.
2229
+ // `/api/v1/vault/assets` table and has no Presets and never will.
2186
2230
  const vault = program
2187
2231
  .command('vault')
2188
- .description('Discover sealed Continuum Packages — including Looks — through the public Vault catalog');
2232
+ .description('Discover sealed Continuum Packages — including Presets — through the public Vault catalog');
2189
2233
  vault
2190
2234
  .command('search')
2191
- .description('Search the Continuum Vault catalog. Finds Looks (pass --package-type look) and every other sealed, listed Continuum Package. Public — no login required')
2235
+ .description('Search the Continuum Vault catalog. Finds Presets (pass --package-type look) and every other sealed, listed Continuum Package. Public — no login required')
2192
2236
  .argument('[query]', 'free-text search over title and tags')
2193
2237
  .option('--package-type <type>', `filter by Continuum Package type. "look" is accepted as an alias for "build" (${vault_1.VAULT_CATALOG_PACKAGE_TYPE_INPUTS.join(', ')})`)
2194
2238
  .option('--sort <mode>', 'recent or name')
@@ -2214,19 +2258,19 @@ vault
2214
2258
  }
2215
2259
  });
2216
2260
  // --- helix look --------------------------------------------------------------
2217
- // A Look is a Universal Item pinned to a Continuum Package of type `build`: a
2261
+ // A Preset is a Universal Item pinned to a Continuum Package of type `build`: a
2218
2262
  // free, never-owned preset — a base plus the exact add-ons installed on it.
2219
2263
  // Its parts are still owned and acquired individually from their own listings;
2220
- // applying a Look never grants, buys, or moves anything the caller does not
2264
+ // applying a Preset never grants, buys, or moves anything the caller does not
2221
2265
  // already own. Contract: run/open-alpha-qa/looks/phase2-contract.txt (items 4
2222
2266
  // and 6) in the workspace root.
2223
2267
  const look = program
2224
2268
  .command('look')
2225
- .description('Read and save Looks — dressed-character presets (API identifier: build)');
2269
+ .description('Read and save Presets — dressed-character presets (API identifier: build)');
2226
2270
  look
2227
2271
  .command('get')
2228
- .description('The dressed character behind one Look in a single public call: base model plus every add-on, with pinned Packages, model URLs/CIDs, and fit data — enough to render it')
2229
- .argument('<ref>', 'Look item id, or packageId@version (an @ splits packageId from an explicit version; without one, the value is read as an item id)')
2272
+ .description('The dressed character behind one Preset in a single public call: base model plus every add-on, with pinned Packages, model URLs/CIDs, and fit data — enough to render it')
2273
+ .argument('<ref>', 'Preset item id, or packageId@version (an @ splits packageId from an explicit version; without one, the value is read as an item id)')
2230
2274
  .option('--json', 'machine-readable output')
2231
2275
  .action(async (ref, opts) => {
2232
2276
  const creds = (0, config_1.loadCredentials)();
@@ -2255,12 +2299,12 @@ look
2255
2299
  });
2256
2300
  look
2257
2301
  .command('save')
2258
- .description("Freeze an inventory instance's current composition into a Look. Never charges a publish fee, never grants a copy of any part, never creates a distribution — it publishes the recipe, not the parts")
2302
+ .description("Freeze an inventory instance's current composition into a Preset. Never charges a publish fee, never grants a copy of any part, never creates a distribution — it publishes the recipe, not the parts")
2259
2303
  .argument('<inventory-item-id>', 'inventory instance to freeze')
2260
- .requiredOption('--title <title>', 'Look title (1-80 chars)')
2261
- .option('--description <text>', 'Look description (<=500 chars)')
2304
+ .requiredOption('--title <title>', 'Preset title (1-80 chars)')
2305
+ .option('--description <text>', 'Preset description (<=500 chars)')
2262
2306
  .option('--public', 'publish publicly (sealed public Package + Vault + Marketplace listings); default is private')
2263
- .option('--look <lookItemId>', 'an existing Look you created — advances it to a new version instead of creating a new one')
2307
+ .option('--look <lookItemId>', 'an existing Preset you created — advances it to a new version instead of creating a new one')
2264
2308
  .option('--json', 'machine-readable output')
2265
2309
  .action(async (inventoryItemId, opts) => {
2266
2310
  const creds = requireAuth();
@@ -2273,7 +2317,7 @@ look
2273
2317
  });
2274
2318
  if (opts.json)
2275
2319
  return printJson(result);
2276
- console.log(`✔ ${result.created ? 'Saved' : 'Updated'} Look ${result.lookItemId} (${result.visibility})`);
2320
+ console.log(`✔ ${result.created ? 'Saved' : 'Updated'} Preset ${result.lookItemId} (${result.visibility})`);
2277
2321
  console.log(` package: ${result.package.packageId}@${result.package.version}`);
2278
2322
  if (result.vaultListingId)
2279
2323
  console.log(` vault listing: ${result.vaultListingId}`);