@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.
- package/dist/addOnPackageVersion.d.ts +47 -0
- package/dist/addOnPackageVersion.js +93 -0
- package/dist/addOnPackageVersion.js.map +1 -0
- package/dist/index.js +64 -20
- package/dist/index.js.map +1 -1
- package/dist/looks.d.ts +9 -7
- package/dist/looks.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
|
@@ -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
|
|
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>', `
|
|
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
|
|
1251
|
-
// "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2229
|
-
.argument('<ref>', '
|
|
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
|
|
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>', '
|
|
2261
|
-
.option('--description <text>', '
|
|
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
|
|
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'}
|
|
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}`);
|