@tarileo/avileo-catalog-cli 0.3.13 → 0.3.14

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
@@ -1867,18 +1867,18 @@ var validateAsync = async (schema, value, _ctx) => {
1867
1867
  return result.issues.length === 0;
1868
1868
  };
1869
1869
  var _encode = (_Err) => {
1870
- const parse3 = _parse(_Err);
1870
+ const parse4 = _parse(_Err);
1871
1871
  const fn = (schema, value, _ctx, _params) => {
1872
1872
  const ctx = _ctx ? { ..._ctx, direction: "backward" } : { direction: "backward" };
1873
- return parse3(schema, value, ctx, finalizeParams(fn, _params));
1873
+ return parse4(schema, value, ctx, finalizeParams(fn, _params));
1874
1874
  };
1875
1875
  return fn;
1876
1876
  };
1877
1877
  var encode = /* @__PURE__ */ _encode($ZodRealError);
1878
1878
  var _decode = (_Err) => {
1879
- const parse3 = _parse(_Err);
1879
+ const parse4 = _parse(_Err);
1880
1880
  const fn = (schema, value, _ctx, _params) => {
1881
- return parse3(schema, value, _ctx, finalizeParams(fn, _params));
1881
+ return parse4(schema, value, _ctx, finalizeParams(fn, _params));
1882
1882
  };
1883
1883
  return fn;
1884
1884
  };
@@ -3373,7 +3373,7 @@ function capSize(inst, size) {
3373
3373
  if (!caps.length)
3374
3374
  return;
3375
3375
  const cap = Math.min(...caps.map((c) => c[0]));
3376
- const parse3 = inst._zod.parse;
3376
+ const parse4 = inst._zod.parse;
3377
3377
  inst._zod.parse = (payload, ctx) => {
3378
3378
  const n = size(payload.value);
3379
3379
  if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
@@ -3387,7 +3387,7 @@ function capSize(inst, size) {
3387
3387
  }
3388
3388
  }
3389
3389
  }
3390
- return parse3(payload, ctx);
3390
+ return parse4(payload, ctx);
3391
3391
  };
3392
3392
  }
3393
3393
  var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
@@ -5076,7 +5076,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5076
5076
  var NONE = 0;
5077
5077
  var ASSUMED = 1;
5078
5078
  var PROVEN = 2;
5079
- function isRecursive(inst, stack, resolve2) {
5079
+ function isRecursive(inst, stack, resolve3) {
5080
5080
  const cached2 = recursive.get(inst);
5081
5081
  if (cached2 !== void 0)
5082
5082
  return cached2 ? PROVEN : NONE;
@@ -5086,7 +5086,7 @@ function isRecursive(inst, stack, resolve2) {
5086
5086
  let result = NONE;
5087
5087
  const check2 = (child) => {
5088
5088
  if (result !== PROVEN && child?._zod) {
5089
- const answer = isRecursive(child, stack, resolve2);
5089
+ const answer = isRecursive(child, stack, resolve3);
5090
5090
  if (answer > result)
5091
5091
  result = answer;
5092
5092
  }
@@ -5097,7 +5097,7 @@ function isRecursive(inst, stack, resolve2) {
5097
5097
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5098
5098
  if (spread && !desc.enumerable)
5099
5099
  continue;
5100
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve2) : NONE;
5100
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve3) : NONE;
5101
5101
  if (child > answer)
5102
5102
  answer = child;
5103
5103
  }
@@ -5161,7 +5161,7 @@ function isRecursive(inst, stack, resolve2) {
5161
5161
  break;
5162
5162
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5163
5163
  case "lazy": {
5164
- const inner = def._cachedInner ?? (resolve2 ? inst._zod.innerType : void 0);
5164
+ const inner = def._cachedInner ?? (resolve3 ? inst._zod.innerType : void 0);
5165
5165
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5166
5166
  break;
5167
5167
  }
@@ -19915,7 +19915,7 @@ var AgentHttpClient = class {
19915
19915
  } catch {
19916
19916
  abort.cancel();
19917
19917
  if (retry && attempt < 2) {
19918
- await new Promise((resolve2) => setTimeout(resolve2, retryDelayMs(attempt)));
19918
+ await new Promise((resolve3) => setTimeout(resolve3, retryDelayMs(attempt)));
19919
19919
  continue;
19920
19920
  }
19921
19921
  throw new AgentNetworkError("No se pudo conectar con la API de Avileo", {
@@ -19926,7 +19926,7 @@ var AgentHttpClient = class {
19926
19926
  }
19927
19927
  abort.cancel();
19928
19928
  if (retry && isRetryableStatus(response.status) && attempt < 2) {
19929
- await new Promise((resolve2) => setTimeout(resolve2, retryDelayMs(attempt, response)));
19929
+ await new Promise((resolve3) => setTimeout(resolve3, retryDelayMs(attempt, response)));
19930
19930
  continue;
19931
19931
  }
19932
19932
  let payload;
@@ -19962,7 +19962,8 @@ var AGENT_CAPABILITY_ACTIONS = [
19962
19962
  "inventory:read",
19963
19963
  "inventory:write",
19964
19964
  "payments:read",
19965
- "payments:write"
19965
+ "payments:write",
19966
+ "sales:read"
19966
19967
  ];
19967
19968
  var AgentCapabilityActionSchema = external_exports.enum(AGENT_CAPABILITY_ACTIONS);
19968
19969
  var AGENT_KEY_MODES = ["read", "mutate"];
@@ -19972,7 +19973,8 @@ var AGENT_KEY_READ_ACTIONS = [
19972
19973
  "catalog:read",
19973
19974
  "assets:read",
19974
19975
  "inventory:read",
19975
- "payments:read"
19976
+ "payments:read",
19977
+ "sales:read"
19976
19978
  ];
19977
19979
  var AgentGrantedActionsSchema = external_exports.array(AgentCapabilityActionSchema).min(1).max(AGENT_CAPABILITY_ACTIONS.length).refine((actions) => new Set(actions).size === actions.length, {
19978
19980
  message: "Las capacidades no pueden repetirse"
@@ -20772,6 +20774,75 @@ var AgentPaymentMethodDtoSchema = external_exports.object({
20772
20774
  qrRenderUrl: external_exports.string().nullable(),
20773
20775
  habilitado: external_exports.boolean()
20774
20776
  }).strict();
20777
+ var uuidSchema2 = external_exports.string().uuid();
20778
+ var timestampSchema2 = external_exports.string().datetime({ offset: true });
20779
+ var nonEmptyTextSchema2 = external_exports.string().trim().min(1);
20780
+ var nonNegativeDecimalStringSchema2 = external_exports.string().regex(/^(?:0|[1-9]\d*)(?:\.\d+)?$/, "Debe ser un decimal no negativo");
20781
+ var AgentSalesChannelSchema = external_exports.enum(["web", "tienda"]);
20782
+ var agentSaleTypeSchema = external_exports.enum(["instant_sale", "pre_order"]);
20783
+ var AgentSalesListQuerySchema = external_exports.object({
20784
+ // Acepta tanto el estado legado como el nombre canónico en v1.
20785
+ status: nonEmptyTextSchema2.max(40).optional(),
20786
+ channel: AgentSalesChannelSchema.optional(),
20787
+ type: agentSaleTypeSchema.optional(),
20788
+ // Rango inclusivo por `since` y exclusivo por `until`.
20789
+ since: timestampSchema2.optional(),
20790
+ until: timestampSchema2.optional(),
20791
+ limit: external_exports.number().int().min(1).max(100).default(20),
20792
+ // Cursor opaco emitido por el backend; no interpretable por el agente.
20793
+ cursor: external_exports.string().min(1).max(512).optional()
20794
+ }).strict();
20795
+ var AgentCustomerMaskedSchema = external_exports.object({
20796
+ name: external_exports.string().nullable(),
20797
+ phone: external_exports.string().nullable(),
20798
+ address: external_exports.string().nullable()
20799
+ }).strict();
20800
+ var AgentSaleItemSchema = external_exports.object({
20801
+ productId: uuidSchema2.nullable(),
20802
+ variantId: uuidSchema2.nullable(),
20803
+ name: nonEmptyTextSchema2,
20804
+ variantName: external_exports.string().nullable(),
20805
+ quantity: nonNegativeDecimalStringSchema2,
20806
+ unitPrice: nonNegativeDecimalStringSchema2,
20807
+ lineTotal: nonNegativeDecimalStringSchema2
20808
+ }).strict();
20809
+ var AgentSaleSummarySchema = external_exports.object({
20810
+ id: uuidSchema2,
20811
+ code: external_exports.string().nullable(),
20812
+ channel: AgentSalesChannelSchema.nullable(),
20813
+ type: agentSaleTypeSchema,
20814
+ // Estado legado de la venta; `commercialStatus` es el canónico actual.
20815
+ legacyStatus: nonEmptyTextSchema2,
20816
+ commercialStatus: external_exports.string().nullable(),
20817
+ paymentStatus: external_exports.string().nullable(),
20818
+ fulfillmentStatus: external_exports.string().nullable(),
20819
+ createdAt: timestampSchema2,
20820
+ currency: external_exports.string().length(3),
20821
+ total: nonNegativeDecimalStringSchema2,
20822
+ itemCount: external_exports.number().int().nonnegative()
20823
+ }).strict();
20824
+ var AgentSaleTimelineEntrySchema = external_exports.object({
20825
+ at: timestampSchema2,
20826
+ kind: nonEmptyTextSchema2,
20827
+ actorKind: external_exports.string().nullable(),
20828
+ // Nota ya enmascarada por el servidor; nunca PII libre.
20829
+ note: external_exports.string().nullable()
20830
+ }).strict();
20831
+ var AgentSaleDetailSchema = AgentSaleSummarySchema.extend({
20832
+ subtotal: nonNegativeDecimalStringSchema2,
20833
+ confirmedAt: timestampSchema2.nullable(),
20834
+ // Método de entrega, NO el `fulfillmentSnapshot` crudo.
20835
+ fulfillmentMethod: external_exports.string().nullable(),
20836
+ deliveryDistrict: external_exports.string().nullable(),
20837
+ customer: AgentCustomerMaskedSchema,
20838
+ items: external_exports.array(AgentSaleItemSchema).max(200),
20839
+ timeline: external_exports.array(AgentSaleTimelineEntrySchema).max(200)
20840
+ }).strict();
20841
+ var AgentSalesPageSchema = external_exports.object({
20842
+ items: external_exports.array(AgentSaleSummarySchema).max(100),
20843
+ total: external_exports.number().int().nonnegative(),
20844
+ nextCursor: external_exports.string().nullable()
20845
+ }).strict();
20775
20846
  var AGENT_DEVICE_AUTH_STATUSES = ["pending", "approved", "denied", "expired"];
20776
20847
  var AGENT_DEVICE_AUTH_DEFAULT_PERMISSIONS = [
20777
20848
  ...AGENT_KEY_READ_ACTIONS
@@ -20834,6 +20905,32 @@ var AgentBusinessResource = class {
20834
20905
  profile() {
20835
20906
  return this.http.request("/api/v1/agent/business/profile");
20836
20907
  }
20908
+ /** Diagnostic state of this credential; never returns key material. */
20909
+ credential() {
20910
+ return this.http.request("/api/v1/agent/credential");
20911
+ }
20912
+ };
20913
+ var AgentSalesResource = class {
20914
+ constructor(http) {
20915
+ this.http = http;
20916
+ }
20917
+ http;
20918
+ list(query = {}) {
20919
+ return this.http.request(
20920
+ `/api/v1/agent/sales${queryString({
20921
+ status: query.status,
20922
+ channel: query.channel,
20923
+ type: query.type,
20924
+ since: query.since,
20925
+ until: query.until,
20926
+ limit: query.limit,
20927
+ cursor: query.cursor
20928
+ })}`
20929
+ );
20930
+ }
20931
+ get(id) {
20932
+ return this.http.request(`/api/v1/agent/sales/${encodeURIComponent(id)}`);
20933
+ }
20837
20934
  };
20838
20935
  async function uploadTemporaryImage(http, file2, options = {}) {
20839
20936
  const body = new FormData();
@@ -21221,6 +21318,7 @@ function createAvileoAgent(config2) {
21221
21318
  const http = new AgentHttpClient(config2);
21222
21319
  return {
21223
21320
  business: new AgentBusinessResource(http),
21321
+ sales: new AgentSalesResource(http),
21224
21322
  operations: new AgentOperationsResource(http),
21225
21323
  products: new AgentProductsResource(http),
21226
21324
  variants: new AgentVariantsResource(http),
@@ -21235,10 +21333,21 @@ function createAvileoAgent(config2) {
21235
21333
  }
21236
21334
 
21237
21335
  // src/config.ts
21238
- import { chmodSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
21336
+ import {
21337
+ chmodSync,
21338
+ existsSync,
21339
+ lstatSync,
21340
+ mkdirSync,
21341
+ readFileSync,
21342
+ realpathSync,
21343
+ renameSync,
21344
+ statSync,
21345
+ unlinkSync,
21346
+ writeFileSync
21347
+ } from "fs";
21239
21348
  import { randomUUID } from "crypto";
21240
- import { homedir } from "os";
21241
- import { dirname, join } from "path";
21349
+ import { homedir, tmpdir } from "os";
21350
+ import { basename, dirname, join, parse as parse3, resolve } from "path";
21242
21351
  import { isIP } from "net";
21243
21352
  var LOCAL_IPV4_HOST_PATTERN = /^(?:127\.|10\.|192\.168\.|169\.254\.|172\.(?:1[6-9]|2\d|3[01])\.|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.)/;
21244
21353
  function isLocalHostname(hostname3) {
@@ -21301,13 +21410,12 @@ function localCredentialPath(env = process.env) {
21301
21410
  "credentials.json"
21302
21411
  );
21303
21412
  }
21304
- function readLocalCredential(env = process.env) {
21305
- let parsed;
21306
- try {
21307
- parsed = JSON.parse(readFileSync(localCredentialPath(env), "utf8"));
21308
- } catch {
21309
- return null;
21310
- }
21413
+ function localCredentialPathSource(env) {
21414
+ if (env.AVILEO_AGENT_CONFIG_PATH?.trim()) return "AVILEO_AGENT_CONFIG_PATH";
21415
+ if (env.XDG_CONFIG_HOME?.trim()) return "XDG_CONFIG_HOME";
21416
+ return "default";
21417
+ }
21418
+ function parseLocalCredential(parsed) {
21311
21419
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
21312
21420
  throw new CliConfigurationError("La credencial local no tiene un formato v\xE1lido");
21313
21421
  }
@@ -21323,6 +21431,60 @@ function readLocalCredential(env = process.env) {
21323
21431
  }
21324
21432
  return { apiKey, baseUrl: normalizeApiBaseUrl(record2.baseUrl) };
21325
21433
  }
21434
+ function readCredentialFile(env) {
21435
+ const path = localCredentialPath(env);
21436
+ const pathSource = localCredentialPathSource(env);
21437
+ let raw;
21438
+ try {
21439
+ raw = readFileSync(path, "utf8");
21440
+ } catch (error62) {
21441
+ const errorCode2 = error62?.code;
21442
+ if (errorCode2 === "ENOENT") return { status: "missing", path, pathSource };
21443
+ return {
21444
+ status: "unreadable",
21445
+ path,
21446
+ pathSource,
21447
+ ...typeof errorCode2 === "string" ? { errorCode: errorCode2 } : {}
21448
+ };
21449
+ }
21450
+ let parsed;
21451
+ try {
21452
+ parsed = JSON.parse(raw);
21453
+ } catch {
21454
+ return { status: "invalid_json", path, pathSource };
21455
+ }
21456
+ try {
21457
+ return { status: "ok", credential: parseLocalCredential(parsed), path, pathSource };
21458
+ } catch (error62) {
21459
+ const failure2 = error62 instanceof CliConfigurationError ? error62 : new CliConfigurationError("La credencial local no tiene un formato v\xE1lido");
21460
+ return { status: "invalid_shape", path, pathSource, errorCode: failure2.code, error: failure2 };
21461
+ }
21462
+ }
21463
+ function inspectLocalCredential(env = process.env) {
21464
+ const result = readCredentialFile(env);
21465
+ if (result.status === "ok") {
21466
+ return {
21467
+ status: "ok",
21468
+ credential: result.credential,
21469
+ path: result.path,
21470
+ pathSource: result.pathSource,
21471
+ ...observedCredentialModes(result.path)
21472
+ };
21473
+ }
21474
+ return {
21475
+ status: result.status,
21476
+ path: result.path,
21477
+ pathSource: result.pathSource,
21478
+ ...result.errorCode === void 0 ? {} : { errorCode: result.errorCode },
21479
+ ...result.status === "missing" ? {} : observedCredentialModes(result.path)
21480
+ };
21481
+ }
21482
+ function readLocalCredential(env = process.env) {
21483
+ const result = readCredentialFile(env);
21484
+ if (result.status === "ok") return result.credential;
21485
+ if (result.error) throw result.error;
21486
+ return null;
21487
+ }
21326
21488
  function resolveCredential(env = process.env) {
21327
21489
  const override = overrideBaseUrl(env);
21328
21490
  if (env.AVILEO_AGENT_REQUIRE_EXPLICIT_ORIGIN === "1" && override === void 0) {
@@ -21350,6 +21512,152 @@ function resolveCredential(env = process.env) {
21350
21512
  }
21351
21513
  return { ...local, source: "local" };
21352
21514
  }
21515
+ var DEFAULT_CREDENTIAL_PATH_HINT = "~/.config/avileo/credentials.json";
21516
+ function formatMode(mode) {
21517
+ const masked = mode & 4095;
21518
+ return masked > 511 ? masked.toString(8) : `0${masked.toString(8).padStart(3, "0")}`;
21519
+ }
21520
+ function observedCredentialModes(path) {
21521
+ const modes = {};
21522
+ const file2 = statSync(path, { throwIfNoEntry: false });
21523
+ if (file2) modes.fileMode = formatMode(file2.mode);
21524
+ const directory = statSync(dirname(path), { throwIfNoEntry: false });
21525
+ if (directory) modes.directoryMode = formatMode(directory.mode);
21526
+ return modes;
21527
+ }
21528
+ function sharedTempRoots() {
21529
+ const roots = [];
21530
+ for (const candidate of ["/tmp", "/var/tmp", tmpdir()]) {
21531
+ try {
21532
+ roots.push(realpathSync(candidate));
21533
+ } catch {
21534
+ }
21535
+ }
21536
+ return roots;
21537
+ }
21538
+ function resolveThroughExistingAncestors(targetPath) {
21539
+ const absolute = resolve(targetPath);
21540
+ const missingSegments = [];
21541
+ let existingAncestor = absolute;
21542
+ while (!existsSync(existingAncestor)) {
21543
+ const parent = dirname(existingAncestor);
21544
+ if (parent === existingAncestor) break;
21545
+ missingSegments.unshift(basename(existingAncestor));
21546
+ existingAncestor = parent;
21547
+ }
21548
+ const realAncestor = realpathSync(existingAncestor);
21549
+ return missingSegments.length === 0 ? realAncestor : resolve(realAncestor, ...missingSegments);
21550
+ }
21551
+ function findRepositoryAncestor(startPath) {
21552
+ let current = startPath;
21553
+ while (true) {
21554
+ if (statSync(join(current, ".git"), { throwIfNoEntry: false })) return current;
21555
+ const parent = dirname(current);
21556
+ if (parent === current) return null;
21557
+ current = parent;
21558
+ }
21559
+ }
21560
+ function assertOutsideRepository(resolvedPath) {
21561
+ const repositoryRoot = findRepositoryAncestor(resolvedPath);
21562
+ if (repositoryRoot === null) return;
21563
+ throw new CliConfigurationError(
21564
+ `No se puede guardar la credencial dentro de un repositorio: ${repositoryRoot} contiene .git. Usa la ruta por defecto ${DEFAULT_CREDENTIAL_PATH_HINT} o define AVILEO_AGENT_CONFIG_PATH fuera del \xE1rbol del repositorio.`
21565
+ );
21566
+ }
21567
+ var UNRESOLVABLE_PARENT_CODES = {
21568
+ EEXIST: true,
21569
+ ELOOP: true,
21570
+ ENOENT: true,
21571
+ ENOTDIR: true
21572
+ };
21573
+ function findBrokenSymlinkAncestor(targetPath) {
21574
+ const absolute = resolve(targetPath);
21575
+ const segments = [];
21576
+ let current = absolute;
21577
+ while (true) {
21578
+ const parent = dirname(current);
21579
+ if (parent === current) break;
21580
+ segments.unshift(basename(current));
21581
+ current = parent;
21582
+ }
21583
+ let walked = parse3(absolute).root;
21584
+ for (const segment of segments) {
21585
+ walked = join(walked, segment);
21586
+ const stats = lstatSync(walked, { throwIfNoEntry: false });
21587
+ if (!stats) return null;
21588
+ if (!stats.isSymbolicLink()) continue;
21589
+ try {
21590
+ realpathSync(walked);
21591
+ } catch {
21592
+ return walked;
21593
+ }
21594
+ }
21595
+ return null;
21596
+ }
21597
+ function describeUnresolvableParent(parentPath, error62) {
21598
+ const code = error62?.code;
21599
+ if (typeof code !== "string" || UNRESOLVABLE_PARENT_CODES[code] !== true) return null;
21600
+ const brokenLink = findBrokenSymlinkAncestor(parentPath);
21601
+ const subject = brokenLink === null ? `${parentPath} contiene un enlace simb\xF3lico` : `${brokenLink} es un enlace simb\xF3lico`;
21602
+ return new CliConfigurationError(
21603
+ `No se pudo preparar el directorio de la credencial ${parentPath}: ${subject} roto o circular. Corrige o elimina ese enlace, o usa la ruta por defecto ${DEFAULT_CREDENTIAL_PATH_HINT} o un directorio privado 0700 fuera del repositorio.`
21604
+ );
21605
+ }
21606
+ function decideParentDirectoryAction(input2) {
21607
+ const fullMode = input2.mode & 4095;
21608
+ if ((fullMode & 63) === 0) return "ok";
21609
+ if (input2.isSharedTempRoot || (fullMode & 512) !== 0) return "refuse-shared";
21610
+ if (input2.ownerUid === input2.processUid) return "repair";
21611
+ return "refuse-foreign";
21612
+ }
21613
+ function assertPrivateParentDirectory(parentPath) {
21614
+ const stats = statSync(parentPath);
21615
+ const fullMode = stats.mode & 4095;
21616
+ const processUid = typeof process.getuid === "function" ? process.getuid() : -1;
21617
+ const action = decideParentDirectoryAction({
21618
+ mode: fullMode,
21619
+ ownerUid: stats.uid,
21620
+ processUid,
21621
+ isSharedTempRoot: sharedTempRoots().includes(parentPath)
21622
+ });
21623
+ if (action === "ok") return;
21624
+ if (action === "refuse-shared") {
21625
+ throw new CliConfigurationError(
21626
+ `El directorio padre de la credencial es un directorio temporal compartido (modo ${formatMode(fullMode)}) pensado para archivos temporales: no guardes credenciales ah\xED ni cambies sus permisos. Usa un subdirectorio privado 0700, por ejemplo mkdtempSync(join(tmpdir(), 'avileo-')), o la ruta por defecto ${DEFAULT_CREDENTIAL_PATH_HINT}.`
21627
+ );
21628
+ }
21629
+ if (action === "refuse-foreign") {
21630
+ throw new CliConfigurationError(
21631
+ `El directorio de credenciales ${parentPath} tiene permisos ${formatMode(fullMode)} y pertenece a otro usuario (uid ${stats.uid}; este proceso usa uid ${processUid}), as\xED que permite acceso de grupo o de otros usuarios y no se puede reparar autom\xE1ticamente. Usa la ruta por defecto ${DEFAULT_CREDENTIAL_PATH_HINT} o un directorio privado propio 0700.`
21632
+ );
21633
+ }
21634
+ chmodSync(parentPath, 448);
21635
+ const repairedMode = statSync(parentPath).mode & 511;
21636
+ if (repairedMode !== 448) {
21637
+ throw new CliConfigurationError(
21638
+ `El directorio de credenciales ${parentPath} qued\xF3 con permisos ${formatMode(repairedMode)} tras repararlo; se requiere 0700.`
21639
+ );
21640
+ }
21641
+ }
21642
+ function assertSafeCredentialPath(targetPath) {
21643
+ const resolvedTarget = resolve(targetPath);
21644
+ const parentPath = dirname(resolvedTarget);
21645
+ try {
21646
+ const resolvedParent = resolveThroughExistingAncestors(parentPath);
21647
+ assertOutsideRepository(resolvedParent);
21648
+ mkdirSync(resolvedParent, { recursive: true, mode: 448 });
21649
+ const realParent = realpathSync(resolvedParent);
21650
+ assertOutsideRepository(realParent);
21651
+ assertPrivateParentDirectory(realParent);
21652
+ } catch (error62) {
21653
+ if (error62 instanceof CliConfigurationError) throw error62;
21654
+ throw describeUnresolvableParent(parentPath, error62) ?? error62;
21655
+ }
21656
+ }
21657
+ function errorCodeOf(error62) {
21658
+ const code = error62?.code;
21659
+ return typeof code === "string" && code.length > 0 ? code : "UNKNOWN";
21660
+ }
21353
21661
  function saveLocalCredential(credential, env = process.env) {
21354
21662
  const apiKey = normalizeAgentKey(
21355
21663
  credential.apiKey,
@@ -21357,13 +21665,46 @@ function saveLocalCredential(credential, env = process.env) {
21357
21665
  );
21358
21666
  const baseUrl = normalizeApiBaseUrl(credential.baseUrl);
21359
21667
  const path = localCredentialPath(env);
21360
- mkdirSync(dirname(path), { recursive: true, mode: 448 });
21668
+ assertSafeCredentialPath(path);
21361
21669
  const temporaryPath = `${path}.${randomUUID()}.tmp`;
21362
- writeFileSync(temporaryPath, `${JSON.stringify({ apiKey, baseUrl })}
21670
+ try {
21671
+ writeFileSync(temporaryPath, `${JSON.stringify({ apiKey, baseUrl })}
21363
21672
  `, { mode: 384 });
21364
- chmodSync(temporaryPath, 384);
21365
- renameSync(temporaryPath, path);
21366
- chmodSync(path, 384);
21673
+ chmodSync(temporaryPath, 384);
21674
+ renameSync(temporaryPath, path);
21675
+ } catch (error62) {
21676
+ throw new CliConfigurationError(
21677
+ `No se pudo guardar la credencial local en ${path} (${errorCodeOf(error62)}).`
21678
+ );
21679
+ } finally {
21680
+ try {
21681
+ unlinkSync(temporaryPath);
21682
+ } catch {
21683
+ }
21684
+ }
21685
+ try {
21686
+ chmodSync(path, 384);
21687
+ } catch (error62) {
21688
+ throw new CliConfigurationError(
21689
+ `La credencial local en ${path} se guard\xF3 pero no se pudo ajustar a 0600 (${errorCodeOf(error62)}). Ejecuta chmod 600 ${path}.`
21690
+ );
21691
+ }
21692
+ const finalMode = statSync(path).mode & 511;
21693
+ if (finalMode !== 384) {
21694
+ throw new CliConfigurationError(
21695
+ `La credencial local en ${path} qued\xF3 con permisos ${formatMode(finalMode)}; se requiere 0600. Ejecuta chmod 600 ${path}.`
21696
+ );
21697
+ }
21698
+ }
21699
+ function inspectLocalCredentialPermissions(env = process.env) {
21700
+ const path = localCredentialPath(env);
21701
+ const { fileMode, directoryMode } = observedCredentialModes(path);
21702
+ if (!fileMode || !directoryMode) {
21703
+ throw new CliConfigurationError(
21704
+ `No se pudieron leer los permisos de la credencial local en ${path}`
21705
+ );
21706
+ }
21707
+ return { path, fileMode, directoryMode };
21367
21708
  }
21368
21709
  function removeLocalCredential(env = process.env) {
21369
21710
  try {
@@ -21548,13 +21889,41 @@ function registerBusinessCommands(root, getClient) {
21548
21889
  // src/commands/catalog.ts
21549
21890
  import { createHash } from "crypto";
21550
21891
  import { readFile as readFile2, stat } from "fs/promises";
21551
- import { basename as basename2, extname } from "path";
21892
+ import { basename as basename3, extname } from "path";
21893
+
21894
+ // src/commands/import-example.ts
21895
+ var CATALOG_IMPORT_EXAMPLE = {
21896
+ source: "supplier-file",
21897
+ products: [
21898
+ {
21899
+ externalRef: { source: "supplier-file", externalId: "SKU-001" },
21900
+ name: "Arroz extra 1 kg",
21901
+ description: "Arroz extra de grano largo, bolsa de 1 kg.",
21902
+ category: { name: "Abarrotes" },
21903
+ unit: "unidad",
21904
+ basePrice: "6.50",
21905
+ costPrice: "5.20",
21906
+ isActive: true,
21907
+ images: [{ kind: "file", path: "./arroz-extra-1kg.jpg" }],
21908
+ variants: [
21909
+ {
21910
+ name: "Bolsa 1 kg",
21911
+ sku: "SKU-001-1KG",
21912
+ unitQuantity: "1",
21913
+ price: "6.50",
21914
+ initialStock: "20",
21915
+ isActive: true
21916
+ }
21917
+ ]
21918
+ }
21919
+ ]
21920
+ };
21552
21921
 
21553
21922
  // src/commands/import-images.ts
21554
21923
  import { randomUUID as randomUUID2 } from "crypto";
21555
21924
  import { constants } from "fs";
21556
21925
  import { open as open2 } from "fs/promises";
21557
- import { basename, dirname as dirname2, resolve } from "path";
21926
+ import { basename as basename2, dirname as dirname2, resolve as resolve2 } from "path";
21558
21927
  var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
21559
21928
  var MAX_TEMPORARY_IMAGES = 100;
21560
21929
  var MAX_TEMPORARY_BYTES = 100 * 1024 * 1024;
@@ -21603,7 +21972,7 @@ async function readImage(path, remainingBytes) {
21603
21972
  const bytes = buffer.subarray(0, length);
21604
21973
  const mimeType = imageMimeType(bytes);
21605
21974
  if (!mimeType) invalidInput("Solo se permiten im\xE1genes JPEG, PNG, WebP o GIF");
21606
- return new File([bytes], basename(path), { type: mimeType });
21975
+ return new File([bytes], basename2(path), { type: mimeType });
21607
21976
  } catch (error62) {
21608
21977
  if (error62 instanceof CliProtocolError) throw error62;
21609
21978
  invalidInput("No se pudo leer una imagen local del cat\xE1logo");
@@ -21614,7 +21983,7 @@ async function readImage(path, remainingBytes) {
21614
21983
  function localImageSources(inputPath) {
21615
21984
  const paths = /* @__PURE__ */ new Map();
21616
21985
  const placeholders = /* @__PURE__ */ new Map();
21617
- const baseDirectory = dirname2(resolve(inputPath));
21986
+ const baseDirectory = dirname2(resolve2(inputPath));
21618
21987
  const normalizeImages = (value) => {
21619
21988
  if (!Array.isArray(value)) return value;
21620
21989
  return value.map((source) => {
@@ -21622,7 +21991,7 @@ function localImageSources(inputPath) {
21622
21991
  if (Object.keys(source).some((key) => key !== "kind" && key !== "path") || typeof source.path !== "string" || source.path.trim().length === 0 || source.path.includes("\0")) {
21623
21992
  invalidInput("Cada imagen local requiere \xFAnicamente kind: file y un path v\xE1lido");
21624
21993
  }
21625
- const path = resolve(baseDirectory, source.path);
21994
+ const path = resolve2(baseDirectory, source.path);
21626
21995
  let placeholder = paths.get(path);
21627
21996
  if (!placeholder) {
21628
21997
  placeholder = randomUUID2();
@@ -22081,7 +22450,7 @@ function registerAssetCommands(catalog, getClient) {
22081
22450
  if (!wantsApply(command)) {
22082
22451
  emitSuccess({
22083
22452
  validated: "local",
22084
- file: basename2(filePath),
22453
+ file: basename3(filePath),
22085
22454
  sizeBytes: fileStat.size
22086
22455
  });
22087
22456
  return;
@@ -22095,7 +22464,7 @@ function registerAssetCommands(catalog, getClient) {
22095
22464
  );
22096
22465
  }
22097
22466
  const contents = await readFile2(filePath);
22098
- const file2 = new File([contents], basename2(filePath), { type: mimeType });
22467
+ const file2 = new File([contents], basename3(filePath), { type: mimeType });
22099
22468
  const rawApprovalId = command.optsWithGlobals().approvalId;
22100
22469
  if (typeof rawApprovalId === "string") {
22101
22470
  emitSuccess(await getClient().assets.upload(file2, requireId(rawApprovalId, "approvalId")));
@@ -22187,6 +22556,7 @@ function registerImportCommands(catalog, getClient) {
22187
22556
  );
22188
22557
  }
22189
22558
  );
22559
+ imports.command("example").description("Imprime el payload can\xF3nico de una importaci\xF3n").action(() => emitSuccess(CATALOG_IMPORT_EXAMPLE));
22190
22560
  imports.command("status <importId>").action(async (importId) => {
22191
22561
  emitSuccess(await getClient().imports.get(requireId(importId, "importId")));
22192
22562
  });
@@ -22222,8 +22592,59 @@ function registerOperationBatchCommands(operation, getClient) {
22222
22592
  });
22223
22593
  }
22224
22594
 
22595
+ // src/commands/sales.ts
22596
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
22597
+ function parseLimit(value) {
22598
+ const parsed = Number(value);
22599
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 100) {
22600
+ throw new CliProtocolError("CLI_VALIDATION_ERROR", "--limit debe ser un entero entre 1 y 100", {
22601
+ limit: value
22602
+ });
22603
+ }
22604
+ return parsed;
22605
+ }
22606
+ function parseChannel(value) {
22607
+ if (value === "web" || value === "tienda") return value;
22608
+ throw new CliProtocolError("CLI_VALIDATION_ERROR", "--channel debe ser web o tienda", {
22609
+ channel: value
22610
+ });
22611
+ }
22612
+ function parseType(value) {
22613
+ if (value === "instant_sale" || value === "pre_order") return value;
22614
+ throw new CliProtocolError("CLI_VALIDATION_ERROR", "--type debe ser instant_sale o pre_order", {
22615
+ type: value
22616
+ });
22617
+ }
22618
+ function requireUuid(value, name) {
22619
+ const trimmed = value.trim();
22620
+ if (!UUID_PATTERN.test(trimmed)) {
22621
+ throw new CliProtocolError("CLI_VALIDATION_ERROR", `${name} debe ser un UUID v\xE1lido`);
22622
+ }
22623
+ return trimmed;
22624
+ }
22625
+ function registerSalesCommands(root, getClient) {
22626
+ const sales = root.command("sales").description("Consulta ventas del negocio (solo lectura, tel\xE9fono parcialmente enmascarado)");
22627
+ sales.command("list").description("Lista ventas con filtros opcionales").option("--status <status>", "Estado de la venta").option("--channel <channel>", "Canal de la venta (web|tienda)", parseChannel).option("--type <type>", "Tipo de venta (instant_sale|pre_order)", parseType).option("--since <iso>", "Fecha m\xEDnima (ISO 8601)").option("--until <iso>", "Fecha m\xE1xima (ISO 8601)").option("--limit <number>", "M\xE1ximo de resultados (1-100)", parseLimit).option("--cursor <cursor>", "Cursor de paginaci\xF3n").action(async (options) => {
22628
+ emitSuccess(
22629
+ await getClient().sales.list({
22630
+ ...options.status === void 0 ? {} : { status: options.status },
22631
+ ...options.channel === void 0 ? {} : { channel: options.channel },
22632
+ ...options.type === void 0 ? {} : { type: options.type },
22633
+ ...options.since === void 0 ? {} : { since: options.since },
22634
+ ...options.until === void 0 ? {} : { until: options.until },
22635
+ ...options.limit === void 0 ? {} : { limit: options.limit },
22636
+ ...options.cursor === void 0 ? {} : { cursor: options.cursor }
22637
+ })
22638
+ );
22639
+ });
22640
+ sales.command("get <id>").description("Consulta el detalle de una venta").action(async (id) => {
22641
+ emitSuccess(await getClient().sales.get(requireUuid(id, "id")));
22642
+ });
22643
+ }
22644
+
22225
22645
  // src/commands/auth.ts
22226
22646
  import { spawn } from "child_process";
22647
+ import { dirname as dirname3 } from "path";
22227
22648
  function safeStartResult(start, browserOpened) {
22228
22649
  return {
22229
22650
  status: "pending",
@@ -22249,7 +22670,7 @@ function safeFinalResult(start, browserOpened, result) {
22249
22670
  };
22250
22671
  }
22251
22672
  function sleep(milliseconds) {
22252
- return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
22673
+ return new Promise((resolve3) => setTimeout(resolve3, milliseconds));
22253
22674
  }
22254
22675
  function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
22255
22676
  if (!process.stdin.isTTY || !process.stderr.isTTY) {
@@ -22258,7 +22679,7 @@ function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
22258
22679
  "auth configure requiere una terminal interactiva; usa AVILEO_AGENT_API_KEY en CI/CD"
22259
22680
  );
22260
22681
  }
22261
- return new Promise((resolve2, reject) => {
22682
+ return new Promise((resolve3, reject) => {
22262
22683
  let value = "";
22263
22684
  const input2 = process.stdin;
22264
22685
  const output2 = process.stderr;
@@ -22272,7 +22693,7 @@ function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
22272
22693
  cleanup();
22273
22694
  output2.write("\n");
22274
22695
  if (result instanceof Error) reject(result);
22275
- else resolve2(result);
22696
+ else resolve3(result);
22276
22697
  };
22277
22698
  const onData = (chunk) => {
22278
22699
  for (const character of String(chunk)) {
@@ -22303,12 +22724,12 @@ function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
22303
22724
  });
22304
22725
  }
22305
22726
  async function openWithExecutable(command, args) {
22306
- return new Promise((resolve2) => {
22727
+ return new Promise((resolve3) => {
22307
22728
  const child = spawn(command, args, { stdio: "ignore", shell: false, detached: true });
22308
- child.once("error", () => resolve2(false));
22729
+ child.once("error", () => resolve3(false));
22309
22730
  child.once("spawn", () => {
22310
22731
  child.unref();
22311
- resolve2(true);
22732
+ resolve3(true);
22312
22733
  });
22313
22734
  });
22314
22735
  }
@@ -22400,7 +22821,15 @@ async function login(options) {
22400
22821
  );
22401
22822
  }
22402
22823
  saveLocalCredential({ apiKey: result.agentKey, baseUrl }, options.env);
22403
- emitSuccess({ ...safeFinalResult(start, browserOpened, result), baseUrl });
22824
+ const permissions = inspectLocalCredentialPermissions(options.env);
22825
+ emitSuccess({
22826
+ ...safeFinalResult(start, browserOpened, result),
22827
+ baseUrl,
22828
+ credentialPath: permissions.path,
22829
+ credentialMode: permissions.fileMode,
22830
+ credentialDirMode: permissions.directoryMode,
22831
+ durability: "La credencial qued\xF3 guardada solo en este equipo: si se pierde el archivo tendr\xE1s que emparejar de nuevo con \xABavileo auth login\xBB."
22832
+ });
22404
22833
  return;
22405
22834
  }
22406
22835
  throw new CliProtocolError("CLI_AUTH_TIMEOUT", "La autorizaci\xF3n expir\xF3 antes de ser aprobada");
@@ -22410,33 +22839,205 @@ async function configure(options) {
22410
22839
  assertIsolatedDevelopmentPairing(options);
22411
22840
  const apiKey = await options.readSecret("Agent Key de Avileo (no se mostrar\xE1): ");
22412
22841
  saveLocalCredential({ apiKey, baseUrl }, options.env);
22842
+ const permissions = inspectLocalCredentialPermissions(options.env);
22413
22843
  emitSuccess({
22414
22844
  configured: true,
22415
22845
  source: "local",
22416
22846
  baseUrl,
22417
- credentialPath: localCredentialPath(options.env)
22847
+ credentialPath: localCredentialPath(options.env),
22848
+ credentialMode: permissions.fileMode,
22849
+ credentialDirMode: permissions.directoryMode
22418
22850
  });
22419
22851
  }
22852
+ var PORTAL_URL_PLACEHOLDER = "<URL_DEL_PORTAL>";
22853
+ function inspectedState(inspection) {
22854
+ switch (inspection.status) {
22855
+ case "ok":
22856
+ return "absent";
22857
+ case "missing":
22858
+ return "absent";
22859
+ default:
22860
+ return inspection.status;
22861
+ }
22862
+ }
22863
+ function defaultCredentialPath(env) {
22864
+ return localCredentialPath({ XDG_CONFIG_HOME: env.XDG_CONFIG_HOME });
22865
+ }
22866
+ function permissionWarning(inspection) {
22867
+ const directoryPath = dirname3(inspection.path);
22868
+ const { fileMode, directoryMode } = inspection;
22869
+ const warnings = [];
22870
+ if (fileMode !== void 0 && (Number.parseInt(fileMode, 8) & 63) !== 0) {
22871
+ warnings.push(
22872
+ `El archivo ${inspection.path} tiene permisos ${fileMode}; ejecuta chmod 600 ${inspection.path}.`
22873
+ );
22874
+ }
22875
+ if (directoryMode !== void 0 && (Number.parseInt(directoryMode, 8) & 63) !== 0) {
22876
+ warnings.push(
22877
+ `El directorio ${directoryPath} tiene permisos ${directoryMode}; ejecuta chmod 700 ${directoryPath}.`
22878
+ );
22879
+ }
22880
+ return warnings.length > 0 ? ` ${warnings.join(" ")}` : "";
22881
+ }
22882
+ function defaultCredentialRecovery(env) {
22883
+ const defaultInspection = inspectLocalCredential({ XDG_CONFIG_HOME: env.XDG_CONFIG_HOME });
22884
+ if (defaultInspection.status !== "ok") return "";
22885
+ return ` Hay una credencial legible en la ruta por defecto (${defaultInspection.path}); para usarla ejecuta \`env -u AVILEO_AGENT_CONFIG_PATH avileo auth status\`.`;
22886
+ }
22887
+ function nextStepForInspection(inspection, env) {
22888
+ switch (inspection.status) {
22889
+ case "missing":
22890
+ if (inspection.pathSource === "AVILEO_AGENT_CONFIG_PATH") {
22891
+ return `AVILEO_AGENT_CONFIG_PATH apunta a ${inspection.path}, pero ese archivo no existe. Ejecuta \`avileo auth login --base-url ${PORTAL_URL_PLACEHOLDER}\` para crearlo en esa ruta, o quita la variable AVILEO_AGENT_CONFIG_PATH para volver a la ruta por defecto (${defaultCredentialPath(env)}).` + defaultCredentialRecovery(env);
22892
+ }
22893
+ return `No hay una credencial guardada en ${inspection.path}. Ejecuta \`avileo auth login --base-url ${PORTAL_URL_PLACEHOLDER}\` para vincular esta m\xE1quina.`;
22894
+ case "unreadable":
22895
+ return `No se pudo leer ${inspection.path}${inspection.errorCode ? ` (${inspection.errorCode})` : ""}. Revisa los permisos del archivo y vuelve a ejecutar \`avileo auth login\`.`;
22896
+ case "invalid_json":
22897
+ return `El archivo ${inspection.path} no contiene JSON v\xE1lido. Vuelve a ejecutar \`avileo auth login\` para regenerarlo.`;
22898
+ case "invalid_shape":
22899
+ return `La credencial en ${inspection.path} no es una Agent Key v\xE1lida o le falta el origen vinculado. Vuelve a ejecutar \`avileo auth login --base-url ${PORTAL_URL_PLACEHOLDER}\`.`;
22900
+ case "ok":
22901
+ return "La credencial local est\xE1 disponible.";
22902
+ }
22903
+ }
22904
+ function describeResolutionFailure(error62, inspection, env) {
22905
+ const originIssue = error62 instanceof CliOriginConflictError || error62 instanceof CliOriginUnsupportedError;
22906
+ if (originIssue || env.AVILEO_AGENT_API_KEY?.trim()) {
22907
+ return { state: "invalid", nextStep: error62.message };
22908
+ }
22909
+ if (inspection.status !== "ok") {
22910
+ return {
22911
+ state: inspectedState(inspection),
22912
+ nextStep: nextStepForInspection(inspection, env)
22913
+ };
22914
+ }
22915
+ return { state: "invalid", nextStep: error62.message };
22916
+ }
22917
+ function lookupFailureState(error62) {
22918
+ if (error62 instanceof AgentNetworkError) return "unreachable";
22919
+ if (error62 instanceof AgentAuthenticationError || error62 instanceof AgentPermissionError) {
22920
+ return "invalid";
22921
+ }
22922
+ const status2 = error62?.status;
22923
+ if (status2 === 401 || status2 === 403) return "invalid";
22924
+ return "unreachable";
22925
+ }
22926
+ function errorCode(error62) {
22927
+ const code = error62 && typeof error62 === "object" && "code" in error62 ? error62.code : void 0;
22928
+ return typeof code === "string" ? code : "LOOKUP_FAILED";
22929
+ }
22930
+ function credentialDiagnostic(value) {
22931
+ if (!value || typeof value !== "object") return null;
22932
+ const record2 = value;
22933
+ const state = record2.state;
22934
+ if (state !== "active" && state !== "expired" && state !== "revoked") return null;
22935
+ return {
22936
+ state,
22937
+ expiresAt: typeof record2.expiresAt === "string" ? record2.expiresAt : null
22938
+ };
22939
+ }
22940
+ function isRejectedCredential(error62) {
22941
+ if (error62 instanceof AgentAuthenticationError || error62 instanceof AgentPermissionError) {
22942
+ return true;
22943
+ }
22944
+ const status2 = error62?.status;
22945
+ return status2 === 401 || status2 === 403;
22946
+ }
22947
+ function nextStepForStatus(state, baseUrl) {
22948
+ if (state === "expired") {
22949
+ return `La Agent Key expir\xF3. Renu\xE9vala volviendo a vincular esta m\xE1quina con \`avileo auth login --base-url ${PORTAL_URL_PLACEHOLDER}\`.`;
22950
+ }
22951
+ if (state === "revoked") {
22952
+ return `La Agent Key fue revocada. Vuelve a vincular esta m\xE1quina con \`avileo auth login --base-url ${PORTAL_URL_PLACEHOLDER}\`.`;
22953
+ }
22954
+ if (state === "invalid") return lookupNextStep("invalid", baseUrl);
22955
+ if (state === "unreachable") return lookupNextStep("unreachable", baseUrl);
22956
+ return lookupNextStep("active", baseUrl);
22957
+ }
22958
+ function lookupNextStep(state, baseUrl) {
22959
+ if (state === "active") {
22960
+ return `La credencial responde contra ${baseUrl}; no hay nada que hacer.`;
22961
+ }
22962
+ if (state === "invalid") {
22963
+ return `El origen ${baseUrl} rechaz\xF3 la credencial. Vuelve a vincular esta m\xE1quina con \`avileo auth login --base-url ${PORTAL_URL_PLACEHOLDER}\`; la clave puede haber sido revocada.`;
22964
+ }
22965
+ return `No se pudo contactar ${baseUrl}. Verifica la conectividad o AVILEO_AGENT_BASE_URL y reintenta con \`avileo auth status\`.`;
22966
+ }
22420
22967
  async function status(options) {
22421
- const credential = resolveCredential(options.env);
22968
+ const inspection = inspectLocalCredential(options.env);
22969
+ const permissionWarningSuffix = permissionWarning(inspection);
22970
+ let credential = null;
22971
+ let resolution;
22972
+ try {
22973
+ credential = resolveCredential(options.env);
22974
+ } catch (error62) {
22975
+ if (!(error62 instanceof CliConfigurationError)) throw error62;
22976
+ resolution = describeResolutionFailure(error62, inspection, options.env);
22977
+ }
22422
22978
  if (!credential) {
22423
- emitSuccess({ configured: false, source: null, business: null });
22979
+ const nextStep = resolution?.nextStep ?? nextStepForInspection(inspection, options.env);
22980
+ emitSuccess({
22981
+ configured: false,
22982
+ state: resolution?.state ?? inspectedState(inspection),
22983
+ source: null,
22984
+ credentialPath: inspection.path,
22985
+ pathSource: inspection.pathSource,
22986
+ credentialMode: inspection.fileMode,
22987
+ credentialDirMode: inspection.directoryMode,
22988
+ baseUrl: null,
22989
+ business: null,
22990
+ nextStep: `${nextStep}${permissionWarningSuffix}`
22991
+ });
22424
22992
  return;
22425
22993
  }
22994
+ const baseUrl = credential.baseUrl ?? AVILEO_AGENT_API_URL;
22995
+ const client = options.getClient();
22426
22996
  let business = null;
22427
22997
  let businessLookupError;
22998
+ let state = "active";
22999
+ let diagnostic = null;
22428
23000
  try {
22429
- business = await options.getClient().business.get();
23001
+ diagnostic = credentialDiagnostic(await client.business.credential());
22430
23002
  } catch (error62) {
22431
- const code = error62 && typeof error62 === "object" && "code" in error62 ? error62.code : void 0;
22432
- businessLookupError = typeof code === "string" ? code : "LOOKUP_FAILED";
23003
+ if (isRejectedCredential(error62)) {
23004
+ state = "invalid";
23005
+ businessLookupError = errorCode(error62);
23006
+ }
22433
23007
  }
23008
+ if (state !== "invalid") {
23009
+ if (diagnostic) {
23010
+ state = diagnostic.state;
23011
+ } else {
23012
+ try {
23013
+ business = await client.business.get();
23014
+ } catch (error62) {
23015
+ businessLookupError = errorCode(error62);
23016
+ state = lookupFailureState(error62);
23017
+ }
23018
+ }
23019
+ }
23020
+ if (diagnostic?.state === "active") {
23021
+ try {
23022
+ business = await client.business.get();
23023
+ } catch (error62) {
23024
+ businessLookupError = errorCode(error62);
23025
+ }
23026
+ }
23027
+ const resolvedNextStep = nextStepForStatus(state, baseUrl);
22434
23028
  emitSuccess({
22435
23029
  configured: true,
22436
- source: credential.source,
22437
- baseUrl: credential.baseUrl ?? AVILEO_AGENT_API_URL,
23030
+ state,
23031
+ source: credential.source === "environment" ? "env" : "local",
23032
+ credentialPath: inspection.path,
23033
+ pathSource: inspection.pathSource,
23034
+ credentialMode: inspection.fileMode,
23035
+ credentialDirMode: inspection.directoryMode,
23036
+ baseUrl,
22438
23037
  business,
22439
- ...businessLookupError ? { businessLookupError } : {}
23038
+ ...businessLookupError ? { businessLookupError } : {},
23039
+ ...diagnostic ? { expiresAt: diagnostic.expiresAt } : {},
23040
+ nextStep: `${resolvedNextStep}${permissionWarningSuffix}`
22440
23041
  });
22441
23042
  }
22442
23043
  async function logout(options) {
@@ -22500,6 +23101,7 @@ function createProgram(options = {}) {
22500
23101
  json: () => Boolean(program.opts().json)
22501
23102
  });
22502
23103
  registerBusinessCommands(program, getClient);
23104
+ registerSalesCommands(program, getClient);
22503
23105
  registerCatalogCommands(program, getClient);
22504
23106
  const operation = program.command("operation").description("Consulta y aplica operaciones aprobadas en Avileo");
22505
23107
  registerOperationBatchCommands(operation, getClient);
@@ -22528,8 +23130,8 @@ function createProgram(options = {}) {
22528
23130
  return await parseAsync3(...args);
22529
23131
  } catch (error62) {
22530
23132
  if (error62 instanceof CliVersionRequested) return program;
22531
- const errorCode = typeof error62 === "object" && error62 !== null && "code" in error62 ? String(error62.code) : "";
22532
- if (errorCode === "commander.helpDisplayed" || errorCode === "commander.version")
23133
+ const errorCode2 = typeof error62 === "object" && error62 !== null && "code" in error62 ? String(error62.code) : "";
23134
+ if (errorCode2 === "commander.helpDisplayed" || errorCode2 === "commander.version")
22533
23135
  return program;
22534
23136
  emitFailure(toFailure(error62));
22535
23137
  process.exitCode = exitCodeFor(error62);