@tarileo/avileo-catalog-cli 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # @tarileo/avileo-catalog-cli
2
2
 
3
- The Avileo tenant-agent command line interface for a registered `tienda` catalog.
3
+ The Avileo tenant-agent command line interface for a registered business with effective catalog-agent access.
4
4
 
5
5
  For a first-time human connection, use the temporary device authorization link:
6
6
 
7
7
  ```sh
8
- npx --yes @tarileo/avileo-catalog-cli@0.3.0 auth login --json
8
+ npx --yes @tarileo/avileo-catalog-cli@0.3.1 auth login --json
9
9
  ```
10
10
 
11
11
  The CLI prints only the verification URI, user code and status, opens the link
@@ -15,7 +15,15 @@ in the URL. Use `auth status --json` to inspect the source (`environment` or
15
15
  `local`) without revealing the key, and `auth logout --json` to remove only the
16
16
  local credential; remote revocation remains an Avileo action.
17
17
 
18
- For CI/CD and automated agents, continue injecting `AVILEO_AGENT_API_KEY`.
18
+ If Avileo has already issued an Agent Key, configure it without putting the
19
+ secret in shell history, process arguments or JSON:
20
+
21
+ ```sh
22
+ avileo auth configure --json
23
+ ```
24
+
25
+ The command reads the key without echoing it and stores it locally. For CI/CD
26
+ and automated agents, continue injecting `AVILEO_AGENT_API_KEY`.
19
27
  Environment credentials always take priority over the local credential file.
20
28
  Never pass an Agent Key through flags, prompts or JSON input.
21
29
 
@@ -29,8 +37,16 @@ reference for headers, idempotency, resources and errors.
29
37
 
30
38
  For operators and AI agents, the recommended sequence is:
31
39
 
32
- 1. Run `business show --json`.
33
- 2. Read the exact resource before proposing a change.
34
- 3. Run mutations without `--apply` to review local validation or a diff.
35
- 4. Request explicit approval and repeat with `--apply`.
36
- 5. Use the durable import preview/apply flow for structured supplier lists.
40
+ 1. Run `business show --json` to confirm the connected business and actions.
41
+ 2. Run `business profile show --json` when evaluating profile completeness.
42
+ 3. Read the exact catalog resource before proposing a change.
43
+ 4. Run direct mutations without `--apply` to review local validation or a diff.
44
+ 5. Add `--apply` to create a proposal; this does not mutate the business.
45
+ 6. Ask the human to approve the exact proposal in Avileo, then apply the
46
+ returned operation or approval ID.
47
+ 7. Use the durable import preview/apply flow for structured supplier lists.
48
+
49
+ Profile updates use a direct JSON object with `business profile update --from
50
+ profile-update.json`. Without `--apply` the command returns a local diff; with
51
+ `--apply` it creates a `business.profile.update` proposal. After approval, run
52
+ `operation apply <id> --json` and re-read the profile.
package/dist/index.js CHANGED
@@ -14729,18 +14729,19 @@ var AGENT_CAPABILITY_ACTIONS = [
14729
14729
  "assets:read",
14730
14730
  "assets:write",
14731
14731
  "inventory:read",
14732
- "inventory:write"
14732
+ "inventory:write",
14733
+ "payments:read",
14734
+ "payments:write"
14733
14735
  ];
14734
14736
  var AgentCapabilityActionSchema = external_exports.enum(AGENT_CAPABILITY_ACTIONS);
14735
14737
  var AGENT_KEY_MODES = ["read", "mutate"];
14736
14738
  var AgentKeyModeSchema = external_exports.enum(AGENT_KEY_MODES);
14737
- var AGENT_CATALOG_KEY_PRESET_ACTIONS = [
14739
+ var AGENT_KEY_READ_ACTIONS = [
14738
14740
  "business:read",
14739
14741
  "catalog:read",
14740
- "catalog:write",
14741
14742
  "assets:read",
14742
- "assets:write",
14743
- "inventory:read"
14743
+ "inventory:read",
14744
+ "payments:read"
14744
14745
  ];
14745
14746
  var AgentGrantedActionsSchema = external_exports.array(AgentCapabilityActionSchema).min(1).max(AGENT_CAPABILITY_ACTIONS.length).refine((actions) => new Set(actions).size === actions.length, {
14746
14747
  message: "Las capacidades no pueden repetirse"
@@ -15221,9 +15222,91 @@ var AgentOperationApplyEnvelopeSchema = external_exports.object({
15221
15222
  success: external_exports.literal(true),
15222
15223
  data: external_exports.record(external_exports.string(), external_exports.unknown())
15223
15224
  }).strict();
15225
+ var AGENT_PAYMENT_LINK_STATUSES = [
15226
+ "awaiting_payment",
15227
+ "verifying",
15228
+ "under_review",
15229
+ "confirmed",
15230
+ "rejected",
15231
+ "expired",
15232
+ "cancelled"
15233
+ ];
15234
+ var AgentPaymentLinkStatusSchema = external_exports.enum(AGENT_PAYMENT_LINK_STATUSES);
15235
+ var AGENT_PAYMENT_LINK_EXPECTED_METHODS = [
15236
+ "yape",
15237
+ "plin",
15238
+ "transferencia",
15239
+ "any"
15240
+ ];
15241
+ var AgentPaymentLinkExpectedMethodSchema = external_exports.enum(AGENT_PAYMENT_LINK_EXPECTED_METHODS);
15242
+ var AGENT_PAYMENT_LINK_CONFIRMED_BY = ["ai", "gateway", "manual", "platform"];
15243
+ var AgentPaymentLinkConfirmedBySchema = external_exports.enum(AGENT_PAYMENT_LINK_CONFIRMED_BY);
15244
+ var AgentPaymentLinkExtractionDtoSchema = external_exports.object({
15245
+ provider: external_exports.enum(["yape", "plin", "bbva_transfer", "unknown"]),
15246
+ /** Detected amount in soles (major units), or null when illegible. */
15247
+ detectedAmount: external_exports.number().nonnegative().multipleOf(0.01).nullable(),
15248
+ currency: external_exports.string().length(3).nullable(),
15249
+ operationNumber: external_exports.string().nullable(),
15250
+ paidAt: timestampSchema.nullable(),
15251
+ payerName: external_exports.string().nullable(),
15252
+ recipientName: external_exports.string().nullable(),
15253
+ confidence: external_exports.enum(["high", "medium", "low"]),
15254
+ signals: external_exports.array(external_exports.string())
15255
+ }).strict();
15256
+ var AgentCreatePaymentLinkInputSchema = external_exports.object({
15257
+ /** Amount in soles (major units); the backend stores amountMinor. */
15258
+ amount: external_exports.number().positive().multipleOf(0.01).max(1e6),
15259
+ currency: external_exports.string().trim().length(3).optional(),
15260
+ concept: nonEmptyTextSchema.max(200).optional(),
15261
+ expectedMethod: AgentPaymentLinkExpectedMethodSchema.optional(),
15262
+ expiresInHours: external_exports.number().int().min(1).max(720).optional(),
15263
+ customerPhone: nonEmptyTextSchema.max(20).optional(),
15264
+ idempotencyKey: nonEmptyTextSchema.max(80).optional()
15265
+ }).strict();
15266
+ var AgentPaymentLinkBaseDtoSchema = external_exports.object({
15267
+ id: uuidSchema,
15268
+ /** Requested amount in soles (major units). */
15269
+ amount: external_exports.number().positive().multipleOf(0.01),
15270
+ currency: external_exports.string().length(3),
15271
+ concept: external_exports.string().nullable(),
15272
+ expectedMethod: AgentPaymentLinkExpectedMethodSchema,
15273
+ status: AgentPaymentLinkStatusSchema,
15274
+ confirmedBy: AgentPaymentLinkConfirmedBySchema.nullable(),
15275
+ extraction: AgentPaymentLinkExtractionDtoSchema.nullable(),
15276
+ expiresAt: timestampSchema,
15277
+ createdAt: timestampSchema,
15278
+ confirmedAt: timestampSchema.nullable().optional()
15279
+ }).strict();
15280
+ var AgentPaymentLinkDtoSchema = AgentPaymentLinkBaseDtoSchema.extend({
15281
+ /** Persisted links can never recover the plaintext token URL. */
15282
+ url: external_exports.null()
15283
+ }).strict();
15284
+ var AgentCreatedPaymentLinkDtoSchema = external_exports.discriminatedUnion("replayed", [
15285
+ AgentPaymentLinkBaseDtoSchema.extend({
15286
+ url: external_exports.string().url(),
15287
+ replayed: external_exports.literal(false)
15288
+ }).strict(),
15289
+ AgentPaymentLinkBaseDtoSchema.extend({
15290
+ url: external_exports.null(),
15291
+ replayed: external_exports.literal(true)
15292
+ }).strict()
15293
+ ]);
15294
+ var isoDateOrTimestampSchema = external_exports.union([
15295
+ external_exports.string().date(),
15296
+ external_exports.string().datetime({ offset: true })
15297
+ ]);
15298
+ var AgentPaymentLinkFilterInputSchema = AgentPaginationInputSchema.extend({
15299
+ status: AgentPaymentLinkStatusSchema.optional(),
15300
+ from: isoDateOrTimestampSchema.optional(),
15301
+ to: isoDateOrTimestampSchema.optional()
15302
+ }).strict();
15303
+ var AgentPaymentLinkPageSchema = external_exports.object({
15304
+ items: external_exports.array(AgentPaymentLinkDtoSchema),
15305
+ total: external_exports.number().int().nonnegative()
15306
+ }).strict();
15224
15307
  var AGENT_DEVICE_AUTH_STATUSES = ["pending", "approved", "denied", "expired"];
15225
15308
  var AGENT_DEVICE_AUTH_DEFAULT_PERMISSIONS = [
15226
- ...AGENT_CATALOG_KEY_PRESET_ACTIONS
15309
+ ...AGENT_KEY_READ_ACTIONS
15227
15310
  ];
15228
15311
  function parsePollResponse(value) {
15229
15312
  if (!AGENT_DEVICE_AUTH_STATUSES.includes(value.status)) {
@@ -15540,6 +15623,32 @@ var AgentOperationsResource = class {
15540
15623
  });
15541
15624
  }
15542
15625
  };
15626
+ var AgentPaymentLinksResource = class {
15627
+ constructor(http) {
15628
+ this.http = http;
15629
+ }
15630
+ http;
15631
+ /**
15632
+ * Creates a payment link. `amount` is in soles (major units); the backend
15633
+ * converts it to minor units — never convert it here. The URL is only
15634
+ * non-null for the initial create response.
15635
+ */
15636
+ create(input) {
15637
+ return this.http.request("/api/v1/agent/payment-links", {
15638
+ method: "POST",
15639
+ body: input,
15640
+ mutation: true
15641
+ }).then((response) => AgentCreatedPaymentLinkDtoSchema.parse(response));
15642
+ }
15643
+ /** Fetches a persisted link; `url` is always `null`. */
15644
+ get(paymentLinkId) {
15645
+ return this.http.request(`/api/v1/agent/payment-links/${encodeURIComponent(paymentLinkId)}`).then((response) => AgentPaymentLinkDtoSchema.parse(response));
15646
+ }
15647
+ /** Lists persisted links; every item has `url: null`. */
15648
+ list(filter = {}) {
15649
+ return this.http.request(`/api/v1/agent/payment-links${queryString(filter)}`).then((response) => AgentPaymentLinkPageSchema.parse(response));
15650
+ }
15651
+ };
15543
15652
  function createAvileoAgent(config2) {
15544
15653
  const http = new AgentHttpClient(config2);
15545
15654
  return {
@@ -15551,7 +15660,8 @@ function createAvileoAgent(config2) {
15551
15660
  tags: new AgentTagsResource(http),
15552
15661
  assets: new AgentAssetsResource(http),
15553
15662
  inventory: new AgentInventoryResource(http),
15554
- imports: new AgentImportsResource(http)
15663
+ imports: new AgentImportsResource(http),
15664
+ paymentLinks: new AgentPaymentLinksResource(http)
15555
15665
  };
15556
15666
  }
15557
15667
 
@@ -15720,34 +15830,8 @@ function redact(value) {
15720
15830
  return value;
15721
15831
  }
15722
15832
 
15723
- // src/commands/business.ts
15724
- function registerBusinessCommands(root, getClient) {
15725
- const business = root.command("business").description("Consulta el negocio vinculado a la clave agente");
15726
- business.command("show").description("Muestra las capacidades seguras del negocio").action(async () => {
15727
- emitSuccess(await getClient().business.get());
15728
- });
15729
- }
15730
-
15731
- // src/commands/catalog.ts
15732
- import { createHash } from "crypto";
15733
- import { readFile, stat } from "fs/promises";
15734
- import { basename, extname } from "path";
15735
- function requireId(value, name) {
15736
- if (!value.trim()) throw new CliProtocolError("CLI_VALIDATION_ERROR", `${name} es obligatorio`);
15737
- return value;
15738
- }
15739
- async function readMutationInput(path) {
15740
- let parsed;
15741
- try {
15742
- parsed = JSON.parse(await readFile(path, "utf8"));
15743
- } catch {
15744
- throw new CliProtocolError("CLI_INPUT_ERROR", "No se pudo leer un objeto JSON desde --from");
15745
- }
15746
- if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
15747
- throw new CliProtocolError("CLI_VALIDATION_ERROR", "--from debe contener un objeto JSON");
15748
- }
15749
- return normalizeInput(parsed);
15750
- }
15833
+ // src/commands/mutation.ts
15834
+ import { readFile } from "fs/promises";
15751
15835
  function normalizeInput(value) {
15752
15836
  const forbidden = {
15753
15837
  businessId: true,
@@ -15770,12 +15854,17 @@ function normalizeInput(value) {
15770
15854
  };
15771
15855
  return visit(value);
15772
15856
  }
15773
- function withMutationOptions(command, includeFrom = true) {
15774
- if (includeFrom) command.requiredOption("--from <json-file>", "Objeto JSON estructurado");
15775
- return command.option("--apply", "Solicita aprobaci\xF3n o ejecuta una mutaci\xF3n aprobada").option("--approval-id <uuid>", "Aprobaci\xF3n emitida por Avileo");
15776
- }
15777
- function wantsApply(command) {
15778
- return Boolean(command.optsWithGlobals().apply);
15857
+ async function readMutationInput(path) {
15858
+ let parsed;
15859
+ try {
15860
+ parsed = JSON.parse(await readFile(path, "utf8"));
15861
+ } catch {
15862
+ throw new CliProtocolError("CLI_INPUT_ERROR", "No se pudo leer un objeto JSON desde --from");
15863
+ }
15864
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
15865
+ throw new CliProtocolError("CLI_VALIDATION_ERROR", "--from debe contener un objeto JSON");
15866
+ }
15867
+ return normalizeInput(parsed);
15779
15868
  }
15780
15869
  function localResult(input, current) {
15781
15870
  if (current === void 0) return { validated: "local", input };
@@ -15785,6 +15874,53 @@ function localResult(input, current) {
15785
15874
  );
15786
15875
  return { validated: "local", current, changes };
15787
15876
  }
15877
+
15878
+ // src/commands/business.ts
15879
+ function profileValues(profile) {
15880
+ const values = profile.values;
15881
+ if (!values || Array.isArray(values) || typeof values !== "object") return {};
15882
+ return values;
15883
+ }
15884
+ function registerBusinessCommands(root, getClient) {
15885
+ const business = root.command("business").description("Consulta el negocio vinculado a la clave agente");
15886
+ business.command("show").description("Muestra las capacidades seguras del negocio").action(async () => {
15887
+ emitSuccess(await getClient().business.get());
15888
+ });
15889
+ const profile = business.command("profile").description("Consulta o propone cambios al perfil del negocio");
15890
+ profile.command("show").description("Muestra campos, valores y completitud del perfil").action(async () => {
15891
+ emitSuccess(await getClient().business.profile());
15892
+ });
15893
+ profile.command("update").description("Previsualiza o propone cambios al perfil").requiredOption("--from <json-file>", "Valores de perfil en un objeto JSON").option("--apply", "Crea una propuesta para aprobaci\xF3n en Avileo").action(async (options, command) => {
15894
+ const values = await readMutationInput(options.from);
15895
+ if (!command.optsWithGlobals().apply) {
15896
+ const current = await getClient().business.profile();
15897
+ emitSuccess(localResult(values, profileValues(current)));
15898
+ return;
15899
+ }
15900
+ emitSuccess(
15901
+ await getClient().operations.propose({
15902
+ operationName: "business.profile.update",
15903
+ payload: { values }
15904
+ })
15905
+ );
15906
+ });
15907
+ }
15908
+
15909
+ // src/commands/catalog.ts
15910
+ import { createHash } from "crypto";
15911
+ import { readFile as readFile2, stat } from "fs/promises";
15912
+ import { basename, extname } from "path";
15913
+ function requireId(value, name) {
15914
+ if (!value.trim()) throw new CliProtocolError("CLI_VALIDATION_ERROR", `${name} es obligatorio`);
15915
+ return value;
15916
+ }
15917
+ function withMutationOptions(command, includeFrom = true) {
15918
+ if (includeFrom) command.requiredOption("--from <json-file>", "Objeto JSON estructurado");
15919
+ return command.option("--apply", "Solicita aprobaci\xF3n o ejecuta una mutaci\xF3n aprobada").option("--approval-id <uuid>", "Aprobaci\xF3n emitida por Avileo");
15920
+ }
15921
+ function wantsApply(command) {
15922
+ return Boolean(command.optsWithGlobals().apply);
15923
+ }
15788
15924
  function addListOptions(command) {
15789
15925
  return command.option("--search <text>").option("--category-id <id>").option("--active <true|false>", "Filtra por estado activo", parseBoolean).option("--limit <number>", "M\xE1ximo de resultados", parseInteger).option("--offset <number>", "Desplazamiento", parseInteger);
15790
15926
  }
@@ -16071,7 +16207,7 @@ function registerAssetCommands(catalog, getClient) {
16071
16207
  "Solo se permiten im\xE1genes JPEG, PNG o WebP"
16072
16208
  );
16073
16209
  }
16074
- const contents = await readFile(filePath);
16210
+ const contents = await readFile2(filePath);
16075
16211
  const file2 = new File([contents], basename(filePath), { type: mimeType });
16076
16212
  const rawApprovalId = command.optsWithGlobals().approvalId;
16077
16213
  if (typeof rawApprovalId === "string") {
@@ -16212,6 +16348,57 @@ function safeFinalResult(start, browserOpened, result) {
16212
16348
  function sleep(milliseconds) {
16213
16349
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
16214
16350
  }
16351
+ function readAgentKeyFromTty(prompt = "Agent Key de Avileo: ") {
16352
+ if (!process.stdin.isTTY || !process.stderr.isTTY) {
16353
+ throw new CliProtocolError(
16354
+ "CLI_CONFIGURATION_ERROR",
16355
+ "auth configure requiere una terminal interactiva; usa AVILEO_AGENT_API_KEY en CI/CD"
16356
+ );
16357
+ }
16358
+ return new Promise((resolve, reject) => {
16359
+ let value = "";
16360
+ const input = process.stdin;
16361
+ const output = process.stderr;
16362
+ const wasRaw = input.isRaw;
16363
+ const cleanup = () => {
16364
+ input.removeListener("data", onData);
16365
+ input.pause();
16366
+ if (wasRaw !== void 0) input.setRawMode(Boolean(wasRaw));
16367
+ };
16368
+ const finish = (result) => {
16369
+ cleanup();
16370
+ output.write("\n");
16371
+ if (result instanceof Error) reject(result);
16372
+ else resolve(result);
16373
+ };
16374
+ const onData = (chunk) => {
16375
+ for (const character of String(chunk)) {
16376
+ if (character === "") {
16377
+ finish(
16378
+ new CliProtocolError(
16379
+ "CLI_CONFIGURATION_ERROR",
16380
+ "Configuraci\xF3n cancelada por el usuario"
16381
+ )
16382
+ );
16383
+ return;
16384
+ }
16385
+ if (character === "\r" || character === "\n") {
16386
+ finish(value);
16387
+ return;
16388
+ }
16389
+ if (character === "\x7F" || character === "\b") {
16390
+ value = value.slice(0, -1);
16391
+ continue;
16392
+ }
16393
+ value += character;
16394
+ }
16395
+ };
16396
+ output.write(prompt);
16397
+ input.setRawMode(true);
16398
+ input.resume();
16399
+ input.on("data", onData);
16400
+ });
16401
+ }
16215
16402
  async function openWithExecutable(command, args) {
16216
16403
  return new Promise((resolve) => {
16217
16404
  const child = spawn(command, args, { stdio: "ignore", shell: false, detached: true });
@@ -16279,6 +16466,21 @@ async function login(options) {
16279
16466
  }
16280
16467
  throw new CliProtocolError("CLI_AUTH_TIMEOUT", "La autorizaci\xF3n expir\xF3 antes de ser aprobada");
16281
16468
  }
16469
+ async function configure(options) {
16470
+ const apiKey = await options.readSecret("Agent Key de Avileo (no se mostrar\xE1): ");
16471
+ saveLocalCredential(
16472
+ {
16473
+ apiKey,
16474
+ ...options.env.AVILEO_AGENT_BASE_URL?.trim() ? { baseUrl: options.env.AVILEO_AGENT_BASE_URL.trim() } : {}
16475
+ },
16476
+ options.env
16477
+ );
16478
+ emitSuccess({
16479
+ configured: true,
16480
+ source: "local",
16481
+ credentialPath: localCredentialPath(options.env)
16482
+ });
16483
+ }
16282
16484
  async function status(options) {
16283
16485
  const credential = resolveCredential(options.env);
16284
16486
  if (!credential) {
@@ -16290,7 +16492,7 @@ async function status(options) {
16290
16492
  try {
16291
16493
  business = await options.getClient().business.get();
16292
16494
  } catch (error51) {
16293
- const code = error51.code;
16495
+ const code = error51 && typeof error51 === "object" && "code" in error51 ? error51.code : void 0;
16294
16496
  businessLookupError = typeof code === "string" ? code : "LOOKUP_FAILED";
16295
16497
  }
16296
16498
  emitSuccess({
@@ -16312,6 +16514,7 @@ async function logout(options) {
16312
16514
  }
16313
16515
  function registerAuthCommands(root, options) {
16314
16516
  const auth = root.command("auth").description("Conecta la CLI con Avileo sin copiar claves en el chat");
16517
+ auth.command("configure").description("Guarda una Agent Key existente sin mostrarla en pantalla").action(() => configure(options));
16315
16518
  auth.command("login").description("Autoriza esta CLI mediante un link temporal").action(() => login(options));
16316
16519
  auth.command("status").description("Muestra el estado de la credencial sin revelar secretos").action(() => status(options));
16317
16520
  auth.command("logout").description("Elimina solamente la credencial local").action(() => logout(options));
@@ -16338,6 +16541,7 @@ function createProgram(options = {}) {
16338
16541
  env: options.env ?? process.env,
16339
16542
  deviceAuthFactory: options.deviceAuthFactory ?? createAvileoAgentDeviceAuth,
16340
16543
  openBrowser: options.openBrowser ?? openVerificationUri,
16544
+ readSecret: options.readSecret ?? readAgentKeyFromTty,
16341
16545
  getClient,
16342
16546
  json: () => Boolean(program.opts().json)
16343
16547
  });