create-panal-agent 0.9.3 → 0.10.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-panal-agent",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "Crea un agente de IA para Panal, funcionando y cobrando on-chain, en cinco minutos",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -10,7 +10,7 @@
10
10
  "typecheck": "tsc --noEmit"
11
11
  },
12
12
  "dependencies": {
13
- "@panal/sdk": "^0.7.0",
13
+ "@panal/sdk": "^0.11.0",
14
14
  "dotenv": "^17.0.0",
15
15
  "tsx": "^4.19.0",
16
16
  "viem": "^2.21.0"
@@ -31,6 +31,7 @@ import {
31
31
  createPanalClient,
32
32
  LoopDetected,
33
33
  MAINNET_ADDRESSES,
34
+ monad,
34
35
  parseEnvelope,
35
36
  parsePaymentHeader,
36
37
  permitNonce,
@@ -54,6 +55,21 @@ const DATA_DIR = process.env.DATA_DIR ?? './data';
54
55
  /** Tope del cuerpo de una petición: sin esto, cualquiera te tumba el proceso. */
55
56
  const MAX_BODY = 256 * 1024;
56
57
 
58
+ /**
59
+ * Tope del encargo, en CARACTERES, y anunciado en `/agent.json`.
60
+ *
61
+ * Va en caracteres y no en bytes porque es lo que el cliente puede contar
62
+ * antes de pagar: el tope de cuerpo de arriba protege el proceso, pero nadie
63
+ * sabe cuántos kilobytes ocupa su texto. Sin un número publicado, un encargo
64
+ * demasiado largo se descubre PAGANDO —el pago queda bloqueado, el agente
65
+ * responde 400, y el cliente espera al plazo para recuperarlo.
66
+ *
67
+ * El límite real lo pone MAX_BODY; este número queda holgadamente por debajo
68
+ * (32k caracteres son unos 128 KB incluso en el peor caso de UTF-8) para que
69
+ * lo que se promete se cumpla siempre, y no solo con texto latino.
70
+ */
71
+ const MAX_BRIEF_CHARS = 32_000;
72
+
57
73
  const key = process.env.AGENT_PRIVATE_KEY?.trim();
58
74
  if (!key || !/^0x[0-9a-fA-F]{64}$/.test(key)) {
59
75
  console.error('Falta AGENT_PRIVATE_KEY (0x + 64 hex) en el .env. Copia .env.example y rellénalo.');
@@ -743,24 +759,47 @@ const server = createServer((req, res) => {
743
759
  // LexPanal tuvo x402 funcionando y nadie lo usó, sencillamente porque no
744
760
  // salía en su tarjeta: un cobro que nadie puede descubrir no existe.
745
761
  if (url.pathname === '/agent.json' && req.method === 'GET') {
762
+ const base = process.env.PUBLIC_URL?.trim().replace(/\/+$/, '') || null;
763
+ const x402 =
764
+ X402_PRICE !== null
765
+ ? {
766
+ method: 'POST' as const,
767
+ path: '/x402/ask',
768
+ ...(base ? { url: `${base}/x402/ask` } : {}),
769
+ scheme: 'eip2612-permit',
770
+ asset: X402_TOKEN,
771
+ assetSymbol: X402_SYMBOL,
772
+ amount: X402_PRICE.toString(),
773
+ payTo: account.address,
774
+ howTo: 'POST {"prompt":"…"} and you get a 402 with the quote. Sign it and repeat with X-Payment.',
775
+ }
776
+ : null;
777
+
746
778
  json(res, 200, {
747
779
  agent: account.address,
748
780
  protocol: 'panal',
749
781
  network: 'monad-mainnet',
750
- ...(X402_PRICE !== null
751
- ? {
752
- x402Ask: {
753
- method: 'POST',
754
- path: '/x402/ask',
755
- scheme: 'eip2612-permit',
756
- asset: X402_TOKEN,
757
- assetSymbol: X402_SYMBOL,
758
- amount: X402_PRICE.toString(),
759
- payTo: account.address,
760
- howTo: 'POST {"prompt":"…"} and you get a 402 with the quote. Sign it and repeat with X-Payment.',
761
- },
762
- }
763
- : {}),
782
+ chainId: monad.id,
783
+ endpoints: {
784
+ base,
785
+ postBrief: {
786
+ method: 'POST',
787
+ path: '/brief/:taskId',
788
+ signMessage: 'Panal brief #<taskId> (EIP-191, firmado por el cliente de la tarea)',
789
+ body: `{"brief": string (máx. ${MAX_BRIEF_CHARS} chars), "address": "0x…", "signature": "0x…"}`,
790
+ maxBriefChars: MAX_BRIEF_CHARS,
791
+ },
792
+ getResult: {
793
+ method: 'GET',
794
+ path: '/result/:taskId',
795
+ signMessage: 'Panal resultado #<taskId> · <epoch> (EIP-191, cabeceras X-Panal-*)',
796
+ },
797
+ ...(x402 ? { x402Ask: x402 } : {}),
798
+ },
799
+ // ALIAS ANTIGUO, en la raíz. Aquí es donde esta plantilla lo publicaba
800
+ // antes, y hay clientes ahí fuera que solo miran este sitio. Se sirve
801
+ // por compatibilidad y desaparecerá; lo que se lee es `endpoints`.
802
+ ...(x402 ? { x402Ask: x402 } : {}),
764
803
  });
765
804
  return;
766
805
  }
@@ -902,6 +941,16 @@ const server = createServer((req, res) => {
902
941
  json(res, 400, { error: 'faltan taskId, brief o signature' });
903
942
  return;
904
943
  }
944
+ // El tope que anuncia /agent.json, aplicado. Se dice el número en la
945
+ // respuesta: el cliente ya pagó, y saber cuánto recortar es la
946
+ // diferencia entre reenviarlo y perder el encargo.
947
+ if (body.brief.length > MAX_BRIEF_CHARS) {
948
+ json(res, 400, {
949
+ error: `el encargo son ${body.brief.length} caracteres y el tope es ${MAX_BRIEF_CHARS}`,
950
+ maxBriefChars: MAX_BRIEF_CHARS,
951
+ });
952
+ return;
953
+ }
905
954
  const taskId = BigInt(idCrudo);
906
955
 
907
956
  // El sobre de la cadena, si este encargo viene de otro agente. Se mira