@tarileo/avileo-catalog-cli 0.3.4 → 0.3.6

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/index.js CHANGED
@@ -6,7 +6,8 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/program.ts
9
- import { Command, CommanderError } from "commander";
9
+ import { readFileSync as readFileSync2 } from "fs";
10
+ import { Command } from "commander";
10
11
 
11
12
  // ../../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
12
13
  var external_exports = {};
@@ -3046,13 +3047,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
3046
3047
  }
3047
3048
  return propValues;
3048
3049
  });
3049
- const isObject3 = isObject;
3050
+ const isObject4 = isObject;
3050
3051
  const catchall = def.catchall;
3051
3052
  let value;
3052
3053
  inst._zod.parse = (payload, ctx) => {
3053
3054
  value ?? (value = _normalized.value);
3054
3055
  const input = payload.value;
3055
- if (!isObject3(input)) {
3056
+ if (!isObject4(input)) {
3056
3057
  payload.issues.push({
3057
3058
  expected: "object",
3058
3059
  code: "invalid_type",
@@ -3179,7 +3180,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
3179
3180
  return (payload, ctx) => fn(shape, payload, ctx);
3180
3181
  };
3181
3182
  let fastpass;
3182
- const isObject3 = isObject;
3183
+ const isObject4 = isObject;
3183
3184
  const jit = !globalConfig.jitless;
3184
3185
  const allowsEval2 = allowsEval;
3185
3186
  const fastEnabled = jit && allowsEval2.value;
@@ -3188,7 +3189,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
3188
3189
  inst._zod.parse = (payload, ctx) => {
3189
3190
  value ?? (value = _normalized.value);
3190
3191
  const input = payload.value;
3191
- if (!isObject3(input)) {
3192
+ if (!isObject4(input)) {
3192
3193
  payload.issues.push({
3193
3194
  expected: "object",
3194
3195
  code: "invalid_type",
@@ -14686,7 +14687,7 @@ var AgentHttpClient = class {
14686
14687
  } catch {
14687
14688
  abort.cancel();
14688
14689
  if (retry && attempt < 2) {
14689
- await new Promise((resolve) => setTimeout(resolve, retryDelayMs(attempt)));
14690
+ await new Promise((resolve2) => setTimeout(resolve2, retryDelayMs(attempt)));
14690
14691
  continue;
14691
14692
  }
14692
14693
  throw new AgentNetworkError("No se pudo conectar con la API de Avileo", {
@@ -14697,7 +14698,7 @@ var AgentHttpClient = class {
14697
14698
  }
14698
14699
  abort.cancel();
14699
14700
  if (retry && isRetryableStatus(response.status) && attempt < 2) {
14700
- await new Promise((resolve) => setTimeout(resolve, retryDelayMs(attempt, response)));
14701
+ await new Promise((resolve2) => setTimeout(resolve2, retryDelayMs(attempt, response)));
14701
14702
  continue;
14702
14703
  }
14703
14704
  let payload;
@@ -14822,6 +14823,73 @@ var UNIT_CATALOG = {
14822
14823
  };
14823
14824
  var UNIT_SLUGS = Object.keys(UNIT_CATALOG);
14824
14825
  var UnitSlugSchema = external_exports.enum(UNIT_SLUGS);
14826
+ var ASSET_METADATA_NAMESPACE = "avileoAsset";
14827
+ var nullableText = (max) => external_exports.string().trim().min(1).max(max).nullable();
14828
+ var tagsSchema = external_exports.array(
14829
+ external_exports.string().trim().min(1).max(48).refine((tag) => !tag.includes(","), "Cada etiqueta debe ir separada, sin comas").transform((tag) => tag.toLowerCase())
14830
+ ).max(20).transform((tags) => [...new Set(tags)]);
14831
+ var AssetVisualSchema = external_exports.object({
14832
+ view: external_exports.enum(["front", "back", "side", "detail", "packaging", "lifestyle", "other"]).nullable(),
14833
+ background: external_exports.enum(["light", "dark", "transparent", "mixed", "other"]).nullable(),
14834
+ hasTransparency: external_exports.boolean().nullable(),
14835
+ containsText: external_exports.boolean().nullable(),
14836
+ containsPrices: external_exports.boolean().nullable()
14837
+ }).strict();
14838
+ var AssetPresentationSchema = external_exports.object({
14839
+ version: external_exports.literal(1),
14840
+ title: nullableText(160),
14841
+ alt: nullableText(500),
14842
+ tags: tagsSchema,
14843
+ visual: AssetVisualSchema,
14844
+ reviewStatus: external_exports.enum(["incomplete", "reviewed"])
14845
+ }).strict();
14846
+ var hasValues = (value) => Object.values(value).some((item) => item !== void 0);
14847
+ var AssetPresentationPatchSchema = AssetPresentationSchema.omit({ version: true }).partial().extend({
14848
+ visual: AssetVisualSchema.partial().refine(hasValues, "Indica al menos un cambio visual").optional()
14849
+ }).strict().refine(hasValues, "Indica al menos un cambio de metadata");
14850
+ var AssetMetadataUpdateInputSchema = external_exports.object({
14851
+ expectedRevision: external_exports.string().trim().min(1).max(128),
14852
+ description: nullableText(1e3).optional(),
14853
+ presentation: AssetPresentationPatchSchema.optional()
14854
+ }).strict().refine(
14855
+ (input) => input.description !== void 0 || input.presentation !== void 0,
14856
+ "Indica al menos un cambio"
14857
+ );
14858
+ var PublicAssetPresentationSchema = AssetPresentationSchema.omit({ reviewStatus: true });
14859
+ var readableVisualSchema = external_exports.object({
14860
+ view: AssetVisualSchema.shape.view.catch(null),
14861
+ background: AssetVisualSchema.shape.background.catch(null),
14862
+ hasTransparency: AssetVisualSchema.shape.hasTransparency.catch(null),
14863
+ containsText: AssetVisualSchema.shape.containsText.catch(null),
14864
+ containsPrices: AssetVisualSchema.shape.containsPrices.catch(null)
14865
+ });
14866
+ var readablePresentationSchema = external_exports.object({
14867
+ version: external_exports.literal(1),
14868
+ title: AssetPresentationSchema.shape.title.catch(null),
14869
+ alt: AssetPresentationSchema.shape.alt.catch(null),
14870
+ tags: AssetPresentationSchema.shape.tags.catch(() => []),
14871
+ visual: readableVisualSchema.catch(() => readableVisualSchema.parse({})),
14872
+ reviewStatus: AssetPresentationSchema.shape.reviewStatus.catch("incomplete")
14873
+ });
14874
+ var metadataEnvelopeSchema = external_exports.object({
14875
+ [ASSET_METADATA_NAMESPACE]: readablePresentationSchema
14876
+ });
14877
+ var CatalogAssetSchema = external_exports.object({
14878
+ id: external_exports.string().uuid(),
14879
+ filename: external_exports.string(),
14880
+ originalName: external_exports.string(),
14881
+ description: external_exports.string().nullable(),
14882
+ url: external_exports.string().url(),
14883
+ mimeType: external_exports.string(),
14884
+ sizeBytes: external_exports.number().int().nonnegative(),
14885
+ width: external_exports.number().int().nonnegative().nullable(),
14886
+ height: external_exports.number().int().nonnegative().nullable(),
14887
+ presentation: AssetPresentationSchema,
14888
+ revision: external_exports.string().min(1).max(128),
14889
+ createdAt: external_exports.string(),
14890
+ updatedAt: external_exports.string(),
14891
+ updatedByActorId: external_exports.string().nullable()
14892
+ }).strict();
14825
14893
  var uuidSchema = external_exports.string().uuid();
14826
14894
  var timestampSchema = external_exports.string().datetime({ offset: true });
14827
14895
  var nonEmptyTextSchema = external_exports.string().trim().min(1);
@@ -14844,7 +14912,13 @@ var AgentAssetDtoSchema = external_exports.object({
14844
14912
  sizeBytes: external_exports.number().int().nonnegative(),
14845
14913
  url: external_exports.string().url(),
14846
14914
  description: nullableTextSchema,
14847
- createdAt: timestampSchema
14915
+ createdAt: timestampSchema,
14916
+ presentation: AssetPresentationSchema.optional(),
14917
+ revision: external_exports.string().min(1).max(128).optional(),
14918
+ updatedAt: timestampSchema.optional(),
14919
+ updatedByActorId: external_exports.string().nullable().optional(),
14920
+ width: external_exports.number().int().positive().nullable().optional(),
14921
+ height: external_exports.number().int().positive().nullable().optional()
14848
14922
  }).strict();
14849
14923
  var AgentCategoryDtoSchema = external_exports.object({
14850
14924
  id: uuidSchema,
@@ -14945,7 +15019,20 @@ var AgentProductFilterInputSchema = AgentPaginationInputSchema.extend({
14945
15019
  isActive: external_exports.coerce.boolean().optional()
14946
15020
  }).strict();
14947
15021
  var AgentAssetFilterInputSchema = AgentPaginationInputSchema.extend({
14948
- search: external_exports.string().trim().min(1).max(255).optional()
15022
+ search: external_exports.string().trim().min(1).max(255).optional(),
15023
+ tags: external_exports.union([external_exports.string().transform((value) => value.split(",")), external_exports.array(external_exports.string())]).pipe(
15024
+ external_exports.array(
15025
+ external_exports.string().trim().min(1).max(48).transform((tag) => tag.toLowerCase())
15026
+ ).max(20)
15027
+ ).transform((tags) => [...new Set(tags)]).optional(),
15028
+ containsPrices: external_exports.union([external_exports.boolean(), external_exports.enum(["true", "false"]).transform((value) => value === "true")]).optional(),
15029
+ reviewStatus: external_exports.enum(["incomplete", "reviewed"]).optional(),
15030
+ mimeType: external_exports.string().trim().min(1).max(100).optional()
15031
+ }).strict();
15032
+ var AgentUpdateAssetInputSchema = AssetMetadataUpdateInputSchema;
15033
+ var AgentUpdateAssetPayloadSchema = external_exports.object({
15034
+ assetId: uuidSchema,
15035
+ body: AgentUpdateAssetInputSchema
14949
15036
  }).strict();
14950
15037
  var AgentInventoryMovementFilterInputSchema = AgentPaginationInputSchema.extend({
14951
15038
  reason: external_exports.string().trim().min(1).max(500).optional()
@@ -14957,6 +15044,18 @@ var AgentTagAssignmentInputSchema = external_exports.object({
14957
15044
  var imageAssetIdsSchema = external_exports.array(uuidSchema).max(8).refine((assetIds) => new Set(assetIds).size === assetIds.length, {
14958
15045
  message: "Las im\xE1genes no pueden repetirse"
14959
15046
  });
15047
+ var AgentProductImageSourceSchema = external_exports.discriminatedUnion("kind", [
15048
+ external_exports.object({ kind: external_exports.literal("asset"), assetId: uuidSchema }).strict(),
15049
+ external_exports.object({ kind: external_exports.literal("temporary"), temporaryImageId: uuidSchema }).strict()
15050
+ ]);
15051
+ var productImagesSchema = external_exports.array(AgentProductImageSourceSchema).max(8).refine(
15052
+ (images) => new Set(
15053
+ images.map(
15054
+ (image) => image.kind === "asset" ? `asset:${image.assetId}` : `temporary:${image.temporaryImageId}`
15055
+ )
15056
+ ).size === images.length,
15057
+ { message: "Las im\xE1genes no pueden repetirse" }
15058
+ );
14960
15059
  var AgentCreateVariantInputSchema = external_exports.object({
14961
15060
  name: nonEmptyTextSchema.max(50),
14962
15061
  sku: external_exports.string().trim().min(1).max(50).optional(),
@@ -14997,8 +15096,11 @@ var AgentUpdateProductInputSchema = external_exports.object({
14997
15096
  isActive: external_exports.boolean().optional(),
14998
15097
  description: nullableTextSchema.optional(),
14999
15098
  imageAssetIds: imageAssetIdsSchema.optional(),
15099
+ images: productImagesSchema.optional(),
15000
15100
  tagAssignments: external_exports.array(AgentTagAssignmentInputSchema).max(20).optional()
15001
- }).strict().refine((input) => Object.keys(input).length > 0, {
15101
+ }).strict().refine((input) => input.images === void 0 || input.imageAssetIds === void 0, {
15102
+ message: "Usa images o imageAssetIds, no ambos"
15103
+ }).refine((input) => Object.keys(input).length > 0, {
15002
15104
  message: "Debes indicar al menos un campo para actualizar"
15003
15105
  });
15004
15106
  var AgentCreateCategoryInputSchema = external_exports.object({
@@ -15040,8 +15142,21 @@ var AgentBackendImportImageSourceSchema = external_exports.discriminatedUnion("k
15040
15142
  external_exports.object({
15041
15143
  kind: external_exports.literal("url"),
15042
15144
  url: httpsUrlSchema
15145
+ }).strict(),
15146
+ external_exports.object({
15147
+ kind: external_exports.literal("temporary"),
15148
+ temporaryImageId: uuidSchema
15043
15149
  }).strict()
15044
15150
  ]);
15151
+ var AgentCatalogImportTemporaryImageDtoSchema = external_exports.object({
15152
+ id: uuidSchema,
15153
+ url: external_exports.string().url(),
15154
+ filename: nonEmptyTextSchema.max(255),
15155
+ mimeType: nonEmptyTextSchema.max(100),
15156
+ sizeBytes: external_exports.number().int().positive(),
15157
+ contentHash: external_exports.string().regex(/^[a-f0-9]{64}$/),
15158
+ expiresAt: timestampSchema
15159
+ }).strict();
15045
15160
  var ExternalRecordRefSchema = external_exports.object({
15046
15161
  source: nonEmptyTextSchema.max(100),
15047
15162
  externalId: external_exports.string().trim().min(1).max(500).optional(),
@@ -15162,6 +15277,7 @@ var AGENT_OPERATION_NAMES = [
15162
15277
  "catalog.tags.delete",
15163
15278
  "catalog.assets.upload",
15164
15279
  "catalog.assets.import",
15280
+ "catalog.assets.update",
15165
15281
  "catalog.inventory.adjust",
15166
15282
  "catalog.inventory.set",
15167
15283
  "catalog.imports.preview",
@@ -15184,6 +15300,15 @@ var AgentOperationProposalInputSchema = external_exports.object({
15184
15300
  operationName: AgentOperationNameSchema,
15185
15301
  payload: external_exports.record(external_exports.string(), external_exports.unknown())
15186
15302
  }).strict();
15303
+ var AgentOperationBatchInputSchema = external_exports.object({
15304
+ operationIds: external_exports.array(external_exports.string().trim().toLowerCase().uuid()).min(1).max(50).transform((ids) => [...new Set(ids)])
15305
+ }).strict();
15306
+ var AgentOperationBatchDtoSchema = external_exports.object({
15307
+ id: uuidSchema,
15308
+ operationIds: external_exports.array(uuidSchema).min(1).max(50),
15309
+ approvalUrl: external_exports.string().url(),
15310
+ createdAt: timestampSchema
15311
+ }).strict();
15187
15312
  var AgentOperationProposalDtoSchema = external_exports.object({
15188
15313
  id: uuidSchema,
15189
15314
  operationName: AgentOperationNameSchema,
@@ -15377,6 +15502,25 @@ var AgentBusinessResource = class {
15377
15502
  return this.http.request("/api/v1/agent/business/profile");
15378
15503
  }
15379
15504
  };
15505
+ async function uploadTemporaryImage(http, file2, options = {}) {
15506
+ const body = new FormData();
15507
+ body.set("file", file2);
15508
+ const result = await http.request("/api/v1/agent/catalog/imports/images", {
15509
+ method: "POST",
15510
+ body,
15511
+ dispatchIdempotency: true,
15512
+ retry: options.retry
15513
+ });
15514
+ const parsed = AgentCatalogImportTemporaryImageDtoSchema.safeParse(result);
15515
+ if (!parsed.success) {
15516
+ throw new AgentError("La API de Avileo devolvi\xF3 una imagen temporal inv\xE1lida", {
15517
+ code: "INVALID_RESPONSE",
15518
+ status: 201,
15519
+ retryable: false
15520
+ });
15521
+ }
15522
+ return parsed.data;
15523
+ }
15380
15524
  var AgentProductsResource = class {
15381
15525
  constructor(http) {
15382
15526
  this.http = http;
@@ -15388,6 +15532,10 @@ var AgentProductsResource = class {
15388
15532
  get(productId) {
15389
15533
  return this.http.request(`/api/v1/agent/catalog/products/${encodeURIComponent(productId)}`);
15390
15534
  }
15535
+ /** Stage bytes for a product gallery proposal; this does not create a permanent asset. */
15536
+ uploadImage(file2, options) {
15537
+ return uploadTemporaryImage(this.http, file2, options);
15538
+ }
15391
15539
  create(input, approvalId) {
15392
15540
  return this.http.request("/api/v1/agent/catalog/products", {
15393
15541
  method: "POST",
@@ -15527,11 +15675,24 @@ var AgentAssetsResource = class {
15527
15675
  }
15528
15676
  http;
15529
15677
  list(filters = {}) {
15530
- return this.http.request(`/api/v1/agent/catalog/assets${queryString(filters)}`);
15678
+ return this.http.request(
15679
+ `/api/v1/agent/catalog/assets${queryString({
15680
+ ...filters,
15681
+ tags: filters.tags?.length ? filters.tags.join(",") : void 0
15682
+ })}`
15683
+ );
15531
15684
  }
15532
15685
  get(assetId) {
15533
15686
  return this.http.request(`/api/v1/agent/catalog/assets/${encodeURIComponent(assetId)}`);
15534
15687
  }
15688
+ update(assetId, input, approvalId) {
15689
+ return this.http.request(`/api/v1/agent/catalog/assets/${encodeURIComponent(assetId)}`, {
15690
+ method: "PATCH",
15691
+ body: input,
15692
+ dispatchIdempotency: true,
15693
+ approvalId
15694
+ });
15695
+ }
15535
15696
  upload(file2, approvalId) {
15536
15697
  const body = new FormData();
15537
15698
  body.set("file", file2);
@@ -15584,6 +15745,9 @@ var AgentImportsResource = class {
15584
15745
  this.http = http;
15585
15746
  }
15586
15747
  http;
15748
+ uploadImage(file2, options) {
15749
+ return uploadTemporaryImage(this.http, file2, options);
15750
+ }
15587
15751
  preview(input) {
15588
15752
  return this.http.request("/api/v1/agent/catalog/imports/preview", {
15589
15753
  method: "POST",
@@ -15625,6 +15789,15 @@ var AgentOperationsResource = class {
15625
15789
  get(operationId) {
15626
15790
  return this.http.request(`/api/v1/agent/operations/${encodeURIComponent(operationId)}`);
15627
15791
  }
15792
+ createBatch(input) {
15793
+ return this.http.request("/api/v1/agent/operation-batches", {
15794
+ method: "POST",
15795
+ body: input
15796
+ });
15797
+ }
15798
+ getBatch(batchId) {
15799
+ return this.http.request(`/api/v1/agent/operation-batches/${encodeURIComponent(batchId)}`);
15800
+ }
15628
15801
  apply(operationId) {
15629
15802
  return this.http.request(`/api/v1/agent/operations/${encodeURIComponent(operationId)}/apply`, {
15630
15803
  method: "POST",
@@ -16036,7 +16209,194 @@ function registerBusinessCommands(root, getClient) {
16036
16209
  // src/commands/catalog.ts
16037
16210
  import { createHash } from "crypto";
16038
16211
  import { readFile as readFile2, stat } from "fs/promises";
16039
- import { basename, extname } from "path";
16212
+ import { basename as basename2, extname } from "path";
16213
+
16214
+ // src/commands/import-images.ts
16215
+ import { randomUUID as randomUUID2 } from "crypto";
16216
+ import { constants } from "fs";
16217
+ import { open } from "fs/promises";
16218
+ import { basename, dirname as dirname2, resolve } from "path";
16219
+ var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
16220
+ var MAX_TEMPORARY_IMAGES = 100;
16221
+ var MAX_TEMPORARY_BYTES = 100 * 1024 * 1024;
16222
+ function isObject3(value) {
16223
+ return value !== null && typeof value === "object" && !Array.isArray(value);
16224
+ }
16225
+ function invalidInput(message) {
16226
+ throw new CliProtocolError("CLI_VALIDATION_ERROR", message);
16227
+ }
16228
+ function imageMimeType(bytes) {
16229
+ if (bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
16230
+ return "image/png";
16231
+ }
16232
+ if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
16233
+ return "image/jpeg";
16234
+ }
16235
+ const signature = bytes.subarray(0, 6).toString("ascii");
16236
+ if (signature === "GIF87a" || signature === "GIF89a") return "image/gif";
16237
+ if (bytes.length >= 12 && bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP") {
16238
+ return "image/webp";
16239
+ }
16240
+ return void 0;
16241
+ }
16242
+ async function readImage(path, remainingBytes) {
16243
+ const handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK).catch(
16244
+ () => invalidInput("No se pudo abrir una imagen local del cat\xE1logo")
16245
+ );
16246
+ try {
16247
+ const stats = await handle.stat();
16248
+ if (!stats.isFile() || stats.size === 0 || stats.size > MAX_IMAGE_BYTES) {
16249
+ invalidInput("Cada imagen local debe ser un archivo de entre 1 byte y 5 MiB");
16250
+ }
16251
+ if (stats.size > remainingBytes) {
16252
+ invalidInput("Las im\xE1genes locales no pueden superar 100 MiB en total");
16253
+ }
16254
+ const buffer = Buffer.alloc(Math.min(stats.size + 1, MAX_IMAGE_BYTES + 1));
16255
+ let length = 0;
16256
+ while (length < buffer.length) {
16257
+ const { bytesRead } = await handle.read(buffer, length, buffer.length - length, null);
16258
+ if (bytesRead === 0) break;
16259
+ length += bytesRead;
16260
+ }
16261
+ if (length === 0 || length !== stats.size || length > MAX_IMAGE_BYTES) {
16262
+ invalidInput("La imagen local cambi\xF3 durante la lectura o supera 5 MiB");
16263
+ }
16264
+ const bytes = buffer.subarray(0, length);
16265
+ const mimeType = imageMimeType(bytes);
16266
+ if (!mimeType) invalidInput("Solo se permiten im\xE1genes JPEG, PNG, WebP o GIF");
16267
+ return new File([bytes], basename(path), { type: mimeType });
16268
+ } catch (error51) {
16269
+ if (error51 instanceof CliProtocolError) throw error51;
16270
+ invalidInput("No se pudo leer una imagen local del cat\xE1logo");
16271
+ } finally {
16272
+ await handle.close();
16273
+ }
16274
+ }
16275
+ function localImageSources(inputPath) {
16276
+ const paths = /* @__PURE__ */ new Map();
16277
+ const placeholders = /* @__PURE__ */ new Map();
16278
+ const baseDirectory = dirname2(resolve(inputPath));
16279
+ const normalizeImages = (value) => {
16280
+ if (!Array.isArray(value)) return value;
16281
+ return value.map((source) => {
16282
+ if (!isObject3(source) || source.kind !== "file") return source;
16283
+ if (Object.keys(source).some((key) => key !== "kind" && key !== "path") || typeof source.path !== "string" || source.path.trim().length === 0 || source.path.includes("\0")) {
16284
+ invalidInput("Cada imagen local requiere \xFAnicamente kind: file y un path v\xE1lido");
16285
+ }
16286
+ const path = resolve(baseDirectory, source.path);
16287
+ let placeholder = paths.get(path);
16288
+ if (!placeholder) {
16289
+ placeholder = randomUUID2();
16290
+ paths.set(path, placeholder);
16291
+ placeholders.set(placeholder, path);
16292
+ }
16293
+ return { kind: "temporary", temporaryImageId: placeholder };
16294
+ });
16295
+ };
16296
+ return { normalizeImages, paths: placeholders };
16297
+ }
16298
+ async function readLocalImages(paths) {
16299
+ if (paths.size > MAX_TEMPORARY_IMAGES) {
16300
+ invalidInput("La importaci\xF3n no puede preparar m\xE1s de 100 im\xE1genes locales \xFAnicas");
16301
+ }
16302
+ const files = /* @__PURE__ */ new Map();
16303
+ let totalBytes = 0;
16304
+ for (const [placeholder, path] of paths) {
16305
+ const file2 = await readImage(path, MAX_TEMPORARY_BYTES - totalBytes);
16306
+ totalBytes += file2.size;
16307
+ files.set(placeholder, file2);
16308
+ }
16309
+ return files;
16310
+ }
16311
+ async function prepareCatalogImport(input, inputPath, imports) {
16312
+ const { normalizeImages, paths } = localImageSources(inputPath);
16313
+ const normalizeProduct = (value, includeVariants) => {
16314
+ if (!isObject3(value)) return value;
16315
+ return {
16316
+ ...value,
16317
+ ..."images" in value ? { images: normalizeImages(value.images) } : {},
16318
+ ...includeVariants && Array.isArray(value.variants) ? { variants: value.variants.map((variant) => normalizeProduct(variant, false)) } : {}
16319
+ };
16320
+ };
16321
+ const candidate = isObject3(input) && Array.isArray(input.products) ? { ...input, products: input.products.map((product) => normalizeProduct(product, true)) } : input;
16322
+ const parsed = AgentCatalogImportPreviewInputSchema.safeParse(candidate);
16323
+ if (!parsed.success) {
16324
+ invalidInput("La lista de importaci\xF3n contiene campos inv\xE1lidos o no permitidos");
16325
+ }
16326
+ const files = await readLocalImages(paths);
16327
+ const uploaded = /* @__PURE__ */ new Map();
16328
+ for (const [placeholder, file2] of files) {
16329
+ const temporary = await imports.uploadImage(file2);
16330
+ uploaded.set(placeholder, temporary.id);
16331
+ }
16332
+ const replaceImages = (images) => images?.map((source) => {
16333
+ if (source.kind !== "temporary") return source;
16334
+ const id = uploaded.get(source.temporaryImageId);
16335
+ return id ? { kind: "temporary", temporaryImageId: id } : source;
16336
+ });
16337
+ return {
16338
+ ...parsed.data,
16339
+ products: parsed.data.products.map((product) => ({
16340
+ ...product,
16341
+ ...product.images ? { images: replaceImages(product.images) } : {},
16342
+ ...product.variants ? {
16343
+ variants: product.variants.map((variant) => ({
16344
+ ...variant,
16345
+ ...variant.images ? { images: replaceImages(variant.images) } : {}
16346
+ }))
16347
+ } : {}
16348
+ }))
16349
+ };
16350
+ }
16351
+
16352
+ // src/commands/product-images.ts
16353
+ async function prepareProductUpdate(input, inputPath, allowFiles = true) {
16354
+ const { normalizeImages, paths } = localImageSources(inputPath);
16355
+ const candidate = {
16356
+ ...input,
16357
+ ..."images" in input ? { images: normalizeImages(input.images) } : {}
16358
+ };
16359
+ if (!allowFiles && paths.size > 0) {
16360
+ throw new CliProtocolError(
16361
+ "CLI_VALIDATION_ERROR",
16362
+ "Para aplicar im\xE1genes ya aprobadas, omite --from y usa --apply --approval-id. Los archivos cambiados requieren una nueva propuesta."
16363
+ );
16364
+ }
16365
+ const parsed = AgentUpdateProductInputSchema.safeParse(candidate);
16366
+ if (!parsed.success) {
16367
+ throw new CliProtocolError(
16368
+ "CLI_VALIDATION_ERROR",
16369
+ "La actualizaci\xF3n contiene campos inv\xE1lidos: usa images o imageAssetIds, con hasta 8 im\xE1genes \xFAnicas y sin URLs."
16370
+ );
16371
+ }
16372
+ const files = await readLocalImages(paths);
16373
+ return { body: parsed.data, files };
16374
+ }
16375
+ function approvedProductUpdate(operation, approvalId, productId) {
16376
+ const input = operation.input;
16377
+ if (operation.id !== approvalId || operation.operationName !== "catalog.products.update" || !input || typeof input !== "object" || Array.isArray(input) || input.productId !== productId || Object.keys(input).some((key) => key !== "productId" && key !== "body")) {
16378
+ throw new CliProtocolError(
16379
+ "CLI_VALIDATION_ERROR",
16380
+ "La aprobaci\xF3n no corresponde a esta actualizaci\xF3n de producto"
16381
+ );
16382
+ }
16383
+ if (operation.state !== "approved" || !Number.isFinite(Date.parse(operation.expiresAt)) || Date.parse(operation.expiresAt) <= Date.now()) {
16384
+ throw new CliProtocolError(
16385
+ "CLI_VALIDATION_ERROR",
16386
+ "La operaci\xF3n debe estar aprobada y vigente antes de aplicar"
16387
+ );
16388
+ }
16389
+ const parsed = AgentUpdateProductInputSchema.safeParse(input.body);
16390
+ if (!parsed.success) {
16391
+ throw new CliProtocolError(
16392
+ "CLI_VALIDATION_ERROR",
16393
+ "El payload aprobado no es una actualizaci\xF3n de producto v\xE1lida"
16394
+ );
16395
+ }
16396
+ return parsed.data;
16397
+ }
16398
+
16399
+ // src/commands/catalog.ts
16040
16400
  function requireId(value, name) {
16041
16401
  if (!value.trim()) throw new CliProtocolError("CLI_VALIDATION_ERROR", `${name} es obligatorio`);
16042
16402
  return value;
@@ -16114,21 +16474,54 @@ function registerProductCommands(catalog, getClient) {
16114
16474
  (approvalId) => getClient().products.create(input, approvalId)
16115
16475
  );
16116
16476
  });
16117
- withMutationOptions(product.command("update <productId>")).action(
16118
- async (productId, options, command) => {
16119
- const id = requireId(productId, "productId");
16120
- const input = await readMutationInput(options.from);
16121
- await applyOrPreview(
16122
- command,
16123
- input,
16124
- getClient,
16125
- "catalog.products.update",
16126
- { productId: id, body: input },
16127
- (approvalId) => getClient().products.update(id, input, approvalId),
16128
- () => getClient().products.get(id)
16129
- );
16477
+ withMutationOptions(
16478
+ product.command("update <productId>").option("--from <json-file>", "Objeto JSON estructurado"),
16479
+ false
16480
+ ).action(async (productId, options, command) => {
16481
+ const id = requireId(productId, "productId");
16482
+ const { approvalId: rawApprovalId } = command.optsWithGlobals();
16483
+ const approvalId = typeof rawApprovalId === "string" ? requireId(rawApprovalId, "approvalId") : void 0;
16484
+ if (!options.from) {
16485
+ if (!wantsApply(command) || !approvalId) {
16486
+ throw new CliProtocolError(
16487
+ "CLI_VALIDATION_ERROR",
16488
+ "--from es obligatorio salvo al aplicar con --apply --approval-id"
16489
+ );
16490
+ }
16491
+ const client = getClient();
16492
+ const operation = await client.operations.get(approvalId);
16493
+ const body = approvedProductUpdate(operation, approvalId, id);
16494
+ emitSuccess(await client.products.update(id, body, approvalId));
16495
+ return;
16130
16496
  }
16131
- );
16497
+ const input = await readMutationInput(options.from);
16498
+ const prepared = await prepareProductUpdate(input, options.from, approvalId === void 0);
16499
+ if (wantsApply(command) && !approvalId) {
16500
+ const uploaded = /* @__PURE__ */ new Map();
16501
+ for (const [placeholder, file2] of prepared.files) {
16502
+ const image = await getClient().products.uploadImage(file2);
16503
+ uploaded.set(placeholder, image.id);
16504
+ }
16505
+ if (prepared.body.images) {
16506
+ prepared.body.images = prepared.body.images.map((source) => {
16507
+ if (source.kind !== "temporary") return source;
16508
+ return {
16509
+ kind: "temporary",
16510
+ temporaryImageId: uploaded.get(source.temporaryImageId) ?? source.temporaryImageId
16511
+ };
16512
+ });
16513
+ }
16514
+ }
16515
+ await applyOrPreview(
16516
+ command,
16517
+ input,
16518
+ getClient,
16519
+ "catalog.products.update",
16520
+ { productId: id, body: prepared.body },
16521
+ (approvalId2) => getClient().products.update(id, prepared.body, approvalId2),
16522
+ () => getClient().products.get(id)
16523
+ );
16524
+ });
16132
16525
  withMutationOptions(product.command("deactivate <productId>"), false).action(
16133
16526
  async (productId, _options, command) => {
16134
16527
  const id = requireId(productId, "productId");
@@ -16305,10 +16698,38 @@ function registerTagCommands(catalog, getClient) {
16305
16698
  }
16306
16699
  function registerAssetCommands(catalog, getClient) {
16307
16700
  const asset = catalog.command("asset");
16308
- asset.command("list").option("--search <text>").option("--limit <number>", "M\xE1ximo de resultados", parseInteger).option("--offset <number>", "Desplazamiento", parseInteger).action(async (options) => emitSuccess(await getClient().assets.list(options)));
16701
+ asset.command("list").option("--search <text>").option("--tags <tags>", "Etiquetas separadas por comas; deben coincidir todas").option("--contains-prices <true|false>", "Presencia de precios conocida", parseBoolean).option("--review-status <status>", "incomplete o reviewed").option("--mime-type <type>", "Formato MIME o prefijo image/").option("--limit <number>", "M\xE1ximo de resultados", parseInteger).option("--offset <number>", "Desplazamiento", parseInteger).action(async (options) => {
16702
+ const parsed = AgentAssetFilterInputSchema.safeParse(options);
16703
+ if (!parsed.success) {
16704
+ throw new CliProtocolError("CLI_VALIDATION_ERROR", "Los filtros de assets no son v\xE1lidos");
16705
+ }
16706
+ emitSuccess(await getClient().assets.list(parsed.data));
16707
+ });
16309
16708
  asset.command("get <assetId>").action(async (assetId) => {
16310
16709
  emitSuccess(await getClient().assets.get(requireId(assetId, "assetId")));
16311
16710
  });
16711
+ withMutationOptions(
16712
+ asset.command("update <assetId>").option("--diff", "Incluye la ficha actual")
16713
+ ).action(async (assetId, options, command) => {
16714
+ const id = requireId(assetId, "assetId");
16715
+ const parsed = AssetMetadataUpdateInputSchema.safeParse(await readMutationInput(options.from));
16716
+ if (!parsed.success) {
16717
+ throw new CliProtocolError(
16718
+ "CLI_VALIDATION_ERROR",
16719
+ "Metadata inv\xE1lida: incluye expectedRevision y cambios de description o presentation"
16720
+ );
16721
+ }
16722
+ const input = parsed.data;
16723
+ await applyOrPreview(
16724
+ command,
16725
+ input,
16726
+ getClient,
16727
+ "catalog.assets.update",
16728
+ { assetId: id, body: input },
16729
+ (approvalId) => getClient().assets.update(id, input, approvalId),
16730
+ options.diff ? () => getClient().assets.get(id) : void 0
16731
+ );
16732
+ });
16312
16733
  withMutationOptions(
16313
16734
  asset.command("upload").requiredOption("--file <path>", "Archivo local"),
16314
16735
  false
@@ -16321,7 +16742,7 @@ function registerAssetCommands(catalog, getClient) {
16321
16742
  if (!wantsApply(command)) {
16322
16743
  emitSuccess({
16323
16744
  validated: "local",
16324
- file: basename(filePath),
16745
+ file: basename2(filePath),
16325
16746
  sizeBytes: fileStat.size
16326
16747
  });
16327
16748
  return;
@@ -16335,7 +16756,7 @@ function registerAssetCommands(catalog, getClient) {
16335
16756
  );
16336
16757
  }
16337
16758
  const contents = await readFile2(filePath);
16338
- const file2 = new File([contents], basename(filePath), { type: mimeType });
16759
+ const file2 = new File([contents], basename2(filePath), { type: mimeType });
16339
16760
  const rawApprovalId = command.optsWithGlobals().approvalId;
16340
16761
  if (typeof rawApprovalId === "string") {
16341
16762
  emitSuccess(await getClient().assets.upload(file2, requireId(rawApprovalId, "approvalId")));
@@ -16406,27 +16827,13 @@ function registerInventoryCommands(catalog, getClient) {
16406
16827
  }
16407
16828
  );
16408
16829
  }
16409
- function rejectLocalFileSources(value) {
16410
- if (Array.isArray(value)) {
16411
- for (const item of value) rejectLocalFileSources(item);
16412
- return;
16413
- }
16414
- if (!value || typeof value !== "object") return;
16415
- const source = value;
16416
- if (source.kind === "file") {
16417
- throw new CliProtocolError(
16418
- "CLI_VALIDATION_ERROR",
16419
- "Sube cada imagen local con catalog asset upload, apru\xE9bala y usa su assetId"
16420
- );
16421
- }
16422
- for (const nested of Object.values(source)) rejectLocalFileSources(nested);
16423
- }
16424
16830
  function registerImportCommands(catalog, getClient) {
16425
16831
  const imports = catalog.command("import");
16426
16832
  imports.command("preview").requiredOption("--from <json-file>", "Lista estructurada para vista previa").action(async (options) => {
16427
16833
  const input = await readMutationInput(options.from);
16428
- rejectLocalFileSources(input);
16429
- emitSuccess(await getClient().imports.preview(input));
16834
+ const client = getClient();
16835
+ const prepared = await prepareCatalogImport(input, options.from, client.imports);
16836
+ emitSuccess(await client.imports.preview(prepared));
16430
16837
  });
16431
16838
  withMutationOptions(imports.command("apply <importId>"), false).action(
16432
16839
  async (importId, _options, command) => {
@@ -16446,6 +16853,36 @@ function registerImportCommands(catalog, getClient) {
16446
16853
  });
16447
16854
  }
16448
16855
 
16856
+ // src/commands/operation-batches.ts
16857
+ function registerOperationBatchCommands(operation, getClient) {
16858
+ const batch = operation.command("batch").description("Agrupa propuestas para revisarlas con un enlace corto");
16859
+ batch.command("create").description("Crea un grupo de revisi\xF3n; no aprueba ni aplica sus operaciones").requiredOption(
16860
+ "--from <json-file>",
16861
+ "Objeto JSON con operationIds de las propuestas existentes"
16862
+ ).action(async (options) => {
16863
+ const parsed = AgentOperationBatchInputSchema.safeParse(
16864
+ await readMutationInput(options.from)
16865
+ );
16866
+ if (!parsed.success) {
16867
+ throw new CliProtocolError(
16868
+ "CLI_VALIDATION_ERROR",
16869
+ parsed.error.issues[0]?.message ?? "Grupo inv\xE1lido"
16870
+ );
16871
+ }
16872
+ emitSuccess(await getClient().operations.createBatch(parsed.data));
16873
+ });
16874
+ batch.command("get <id>").description("Consulta un grupo y recupera su enlace corto de revisi\xF3n").action(async (id) => {
16875
+ const parsed = AgentOperationBatchDtoSchema.shape.id.safeParse(id.trim());
16876
+ if (!parsed.success) {
16877
+ throw new CliProtocolError(
16878
+ "CLI_VALIDATION_ERROR",
16879
+ "El identificador del grupo debe ser un UUID v\xE1lido"
16880
+ );
16881
+ }
16882
+ emitSuccess(await getClient().operations.getBatch(parsed.data));
16883
+ });
16884
+ }
16885
+
16449
16886
  // src/commands/auth.ts
16450
16887
  import { spawn } from "child_process";
16451
16888
  function safeStartResult(start, browserOpened) {
@@ -16473,7 +16910,7 @@ function safeFinalResult(start, browserOpened, result) {
16473
16910
  };
16474
16911
  }
16475
16912
  function sleep(milliseconds) {
16476
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
16913
+ return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
16477
16914
  }
16478
16915
  function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
16479
16916
  if (!process.stdin.isTTY || !process.stderr.isTTY) {
@@ -16482,7 +16919,7 @@ function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
16482
16919
  "auth configure requiere una terminal interactiva; usa AVILEO_AGENT_API_KEY en CI/CD"
16483
16920
  );
16484
16921
  }
16485
- return new Promise((resolve, reject) => {
16922
+ return new Promise((resolve2, reject) => {
16486
16923
  let value = "";
16487
16924
  const input = process.stdin;
16488
16925
  const output = process.stderr;
@@ -16496,7 +16933,7 @@ function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
16496
16933
  cleanup();
16497
16934
  output.write("\n");
16498
16935
  if (result instanceof Error) reject(result);
16499
- else resolve(result);
16936
+ else resolve2(result);
16500
16937
  };
16501
16938
  const onData = (chunk) => {
16502
16939
  for (const character of String(chunk)) {
@@ -16527,12 +16964,12 @@ function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
16527
16964
  });
16528
16965
  }
16529
16966
  async function openWithExecutable(command, args) {
16530
- return new Promise((resolve) => {
16967
+ return new Promise((resolve2) => {
16531
16968
  const child = spawn(command, args, { stdio: "ignore", shell: false, detached: true });
16532
- child.once("error", () => resolve(false));
16969
+ child.once("error", () => resolve2(false));
16533
16970
  child.once("spawn", () => {
16534
16971
  child.unref();
16535
- resolve(true);
16972
+ resolve2(true);
16536
16973
  });
16537
16974
  });
16538
16975
  }
@@ -16673,6 +17110,12 @@ function registerAuthCommands(root, options) {
16673
17110
  }
16674
17111
 
16675
17112
  // src/program.ts
17113
+ var CliVersionRequested = class extends Error {
17114
+ constructor() {
17115
+ super("version requested");
17116
+ this.name = "CliVersionRequested";
17117
+ }
17118
+ };
16676
17119
  function createProgram(options = {}) {
16677
17120
  const program = new Command();
16678
17121
  let client;
@@ -16683,10 +17126,15 @@ function createProgram(options = {}) {
16683
17126
  });
16684
17127
  return client;
16685
17128
  });
16686
- program.name("avileo").description("CLI seguro para el cat\xE1logo de un negocio Avileo").option("--json", "Emite la envoltura JSON versionada para automatizaci\xF3n");
17129
+ const packageJson = JSON.parse(
17130
+ readFileSync2(new URL("../package.json", import.meta.url), "utf8")
17131
+ );
17132
+ const cliVersion = typeof packageJson === "object" && packageJson !== null && "version" in packageJson && typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
17133
+ program.name("avileo").description("CLI seguro para el cat\xE1logo de un negocio Avileo").version(cliVersion, "--version", "Muestra la versi\xF3n del paquete publicado").option("--json", "Emite la envoltura JSON versionada para automatizaci\xF3n");
16687
17134
  program.configureOutput({ writeErr: () => void 0 });
16688
17135
  program.exitOverride((error51) => {
16689
17136
  if (error51.code === "commander.helpDisplayed") return;
17137
+ if (error51.code === "commander.version") throw new CliVersionRequested();
16690
17138
  throw new CliProtocolError("CLI_USAGE_ERROR", error51.message);
16691
17139
  });
16692
17140
  registerAuthCommands(program, {
@@ -16700,6 +17148,7 @@ function createProgram(options = {}) {
16700
17148
  registerBusinessCommands(program, getClient);
16701
17149
  registerCatalogCommands(program, getClient);
16702
17150
  const operation = program.command("operation").description("Consulta y aplica operaciones aprobadas en Avileo");
17151
+ registerOperationBatchCommands(operation, getClient);
16703
17152
  operation.command("status <id>").description("Consulta el estado de una operaci\xF3n").action(async (id) => {
16704
17153
  if (!id.trim()) {
16705
17154
  throw new CliProtocolError("CLI_VALIDATION_ERROR", "id es obligatorio");
@@ -16724,7 +17173,9 @@ function createProgram(options = {}) {
16724
17173
  try {
16725
17174
  return await parseAsync3(...args);
16726
17175
  } catch (error51) {
16727
- if (error51 instanceof CommanderError && error51.code === "commander.helpDisplayed")
17176
+ if (error51 instanceof CliVersionRequested) return program;
17177
+ const errorCode = typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : "";
17178
+ if (errorCode === "commander.helpDisplayed" || errorCode === "commander.version")
16728
17179
  return program;
16729
17180
  emitFailure(toFailure(error51));
16730
17181
  process.exitCode = exitCodeFor(error51);