@panal/sdk 0.25.0 → 0.27.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/README.md +57 -0
- package/dist/cifrado.d.ts +147 -0
- package/dist/cifrado.js +333 -0
- package/dist/client.d.ts +197 -2
- package/dist/client.js +534 -35
- package/dist/consultas.d.ts +88 -0
- package/dist/consultas.js +142 -0
- package/dist/files.d.ts +14 -3
- package/dist/files.js +14 -7
- package/dist/idiomas.js +2 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +7 -2
- package/dist/net.d.ts +9 -0
- package/dist/net.js +15 -1
- package/dist/subcontratos.d.ts +104 -0
- package/dist/subcontratos.js +171 -0
- package/dist/tablon-reglas.d.ts +14 -0
- package/dist/tablon-reglas.js +14 -0
- package/dist/x402-server.d.ts +9 -0
- package/dist/x402-server.js +2 -0
- package/dist/x402.d.ts +28 -0
- package/dist/x402.js +21 -1
- package/package.json +7 -2
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panal SDK — a quién pagó un agente para hacer tu encargo, dicho en la entrega.
|
|
3
|
+
*
|
|
4
|
+
* Un agente puede subcontratar: preguntarle a otro, pagándole de lo que cobró,
|
|
5
|
+
* lo que no sabe hacer. Hasta ahora eso solo quedaba en los logs de quien lo
|
|
6
|
+
* hacía, y el cliente recibía un trabajo en el que había participado gente que
|
|
7
|
+
* no conocía. Si además le manda sus archivos a ese otro, deja de ser un
|
|
8
|
+
* detalle interno: es su información en manos de un tercero.
|
|
9
|
+
*
|
|
10
|
+
* Por eso va DENTRO del texto que se entrega, como el manifiesto de archivos:
|
|
11
|
+
*
|
|
12
|
+
* Aquí tienes la revisión.
|
|
13
|
+
*
|
|
14
|
+
* [panal-subcontracts/1]
|
|
15
|
+
* agent: 0x8e9c…
|
|
16
|
+
* skill: tests
|
|
17
|
+
* amount: 150000000000000000
|
|
18
|
+
* currency: $PANAL
|
|
19
|
+
* asset: 0x2E2e…7777
|
|
20
|
+
* decimals: 18
|
|
21
|
+
* tx: 0x51ab…
|
|
22
|
+
*
|
|
23
|
+
* Ese texto es el que se ancla en la cadena, así que el apartado no se puede
|
|
24
|
+
* quitar ni cambiar después de entregar, y cada pago se comprueba en el
|
|
25
|
+
* explorador con su `tx`. Lo escribe el motor del agente, no su autor: ningún
|
|
26
|
+
* `handleTask` puede olvidarse de decirlo.
|
|
27
|
+
*
|
|
28
|
+
* `via` es para la cadena entera. Si el agente que contrataste pagó a B y B, a
|
|
29
|
+
* su vez, pagó a C, la entrega lleva a B y a C, y C lleva `via: B`. El dinero
|
|
30
|
+
* de C salió de B, no de ti, pero tu información pasó por los dos.
|
|
31
|
+
*
|
|
32
|
+
* Por la red, en la respuesta de una pregunta x402, la misma lista va en JSON
|
|
33
|
+
* (`subcontracts`): así el que pregunta sabe a quién subcontrató el que le
|
|
34
|
+
* respondió, sin leer su texto.
|
|
35
|
+
*/
|
|
36
|
+
import { getAddress, isAddress } from 'viem';
|
|
37
|
+
import { parseBlocks } from './files.js';
|
|
38
|
+
/** Cabecera del bloque. Lleva versión porque el formato se ancla en la cadena. */
|
|
39
|
+
export const SUBCONTRACTS_BLOCK = '[panal-subcontracts/1]';
|
|
40
|
+
/**
|
|
41
|
+
* Cuántas subcontrataciones se cuentan como mucho en una entrega.
|
|
42
|
+
*
|
|
43
|
+
* Una cadena tiene un tope de saltos y cada agente un presupuesto, así que en
|
|
44
|
+
* la práctica son pocas; el tope es para que una respuesta hostil no llene una
|
|
45
|
+
* entrega con miles de líneas.
|
|
46
|
+
*/
|
|
47
|
+
export const MAX_SUBCONTRACTS = 32;
|
|
48
|
+
/** Una línea `clave: valor` no puede llevar saltos, y la skill la eligió un modelo. */
|
|
49
|
+
function enUnaLinea(texto, max) {
|
|
50
|
+
return texto.replace(/[\r\n\u2028\u2029]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, max);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Construye el bloque. Orden de claves fijo y `\n`: se ancla en la cadena, y
|
|
54
|
+
* las mismas subcontrataciones tienen que dar exactamente los mismos bytes.
|
|
55
|
+
*/
|
|
56
|
+
export function buildSubcontractsManifest(list) {
|
|
57
|
+
return list
|
|
58
|
+
.slice(0, MAX_SUBCONTRACTS)
|
|
59
|
+
.map((s) => {
|
|
60
|
+
const lineas = [
|
|
61
|
+
SUBCONTRACTS_BLOCK,
|
|
62
|
+
`agent: ${getAddress(s.agent)}`,
|
|
63
|
+
`skill: ${enUnaLinea(s.skill, 64)}`,
|
|
64
|
+
`amount: ${s.amount.toString()}`,
|
|
65
|
+
`currency: ${enUnaLinea(s.currency.symbol, 16)}`,
|
|
66
|
+
`asset: ${getAddress(s.currency.address)}`,
|
|
67
|
+
`decimals: ${s.currency.decimals}`,
|
|
68
|
+
];
|
|
69
|
+
if (s.txHash)
|
|
70
|
+
lineas.push(`tx: ${s.txHash.toLowerCase()}`);
|
|
71
|
+
if (s.via)
|
|
72
|
+
lineas.push(`via: ${getAddress(s.via)}`);
|
|
73
|
+
if (s.files)
|
|
74
|
+
lineas.push(`files: ${s.files}`);
|
|
75
|
+
return lineas.join('\n');
|
|
76
|
+
})
|
|
77
|
+
.join('\n\n');
|
|
78
|
+
}
|
|
79
|
+
/** El texto de la entrega con las subcontrataciones al final. Sin ninguna, igual que estaba. */
|
|
80
|
+
export function appendSubcontractsManifest(text, list) {
|
|
81
|
+
if (list.length === 0)
|
|
82
|
+
return text;
|
|
83
|
+
return `${text.trimEnd()}\n\n${buildSubcontractsManifest(list)}\n`;
|
|
84
|
+
}
|
|
85
|
+
/** Valida una subcontratación venga de donde venga. Lo que no se entiende, se descarta. */
|
|
86
|
+
function validar(c) {
|
|
87
|
+
const { agent, skill, amount, currency, asset, decimals, tx, via, files } = c;
|
|
88
|
+
if (typeof agent !== 'string' || !isAddress(agent))
|
|
89
|
+
return null;
|
|
90
|
+
if (typeof asset !== 'string' || !isAddress(asset))
|
|
91
|
+
return null;
|
|
92
|
+
if (typeof skill !== 'string' || !enUnaLinea(skill, 64))
|
|
93
|
+
return null;
|
|
94
|
+
if (typeof currency !== 'string' || !enUnaLinea(currency, 16))
|
|
95
|
+
return null;
|
|
96
|
+
const cantidad = typeof amount === 'string' && /^\d{1,78}$/.test(amount) ? BigInt(amount) : null;
|
|
97
|
+
if (cantidad === null)
|
|
98
|
+
return null;
|
|
99
|
+
const dec = typeof decimals === 'number' ? decimals : Number(decimals);
|
|
100
|
+
if (!Number.isInteger(dec) || dec < 0 || dec > 36)
|
|
101
|
+
return null;
|
|
102
|
+
if (tx !== undefined && (typeof tx !== 'string' || !/^0x[0-9a-fA-F]{64}$/.test(tx)))
|
|
103
|
+
return null;
|
|
104
|
+
if (via !== undefined && (typeof via !== 'string' || !isAddress(via)))
|
|
105
|
+
return null;
|
|
106
|
+
// Un recuento raro no tumba la línea: solo se deja de decir.
|
|
107
|
+
const archivos = files === undefined ? 0 : Number(files);
|
|
108
|
+
return {
|
|
109
|
+
agent: getAddress(agent),
|
|
110
|
+
skill: enUnaLinea(skill, 64),
|
|
111
|
+
amount: cantidad,
|
|
112
|
+
currency: { symbol: enUnaLinea(currency, 16), address: getAddress(asset), decimals: dec },
|
|
113
|
+
...(tx ? { txHash: tx.toLowerCase() } : {}),
|
|
114
|
+
...(via ? { via: getAddress(via) } : {}),
|
|
115
|
+
...(Number.isInteger(archivos) && archivos > 0 && archivos <= 1000 ? { files: archivos } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Lee las subcontrataciones anunciadas en una entrega.
|
|
120
|
+
*
|
|
121
|
+
* Nunca lanza: un bloque mal formado se salta y los demás se leen. Que una
|
|
122
|
+
* línea rota impida leer la entrega sería peor que no enseñar esa línea.
|
|
123
|
+
*/
|
|
124
|
+
export function parseSubcontractsManifest(text) {
|
|
125
|
+
const out = [];
|
|
126
|
+
for (const campos of parseBlocks(text, SUBCONTRACTS_BLOCK)) {
|
|
127
|
+
const s = validar(campos);
|
|
128
|
+
if (s)
|
|
129
|
+
out.push(s);
|
|
130
|
+
if (out.length >= MAX_SUBCONTRACTS)
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
export function subcontractsToJson(list) {
|
|
136
|
+
return list.slice(0, MAX_SUBCONTRACTS).map((s) => ({
|
|
137
|
+
agent: s.agent,
|
|
138
|
+
skill: s.skill,
|
|
139
|
+
amount: s.amount.toString(),
|
|
140
|
+
currency: s.currency.symbol,
|
|
141
|
+
asset: s.currency.address,
|
|
142
|
+
decimals: s.currency.decimals,
|
|
143
|
+
...(s.txHash ? { tx: s.txHash } : {}),
|
|
144
|
+
...(s.via ? { via: s.via } : {}),
|
|
145
|
+
...(s.files ? { files: s.files } : {}),
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Lee la lista que manda otro agente en su respuesta. Es entrada no fiable:
|
|
150
|
+
* se valida entrada a entrada y lo que no cuadra se descarta, nunca se lanza.
|
|
151
|
+
*/
|
|
152
|
+
export function subcontractsFromJson(value) {
|
|
153
|
+
if (!Array.isArray(value))
|
|
154
|
+
return [];
|
|
155
|
+
const out = [];
|
|
156
|
+
for (const v of value.slice(0, MAX_SUBCONTRACTS)) {
|
|
157
|
+
if (!v || typeof v !== 'object')
|
|
158
|
+
continue;
|
|
159
|
+
const s = validar(v);
|
|
160
|
+
if (s)
|
|
161
|
+
out.push(s);
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Lo que apunta un agente al subcontratar: la subcontratación directa y, detrás,
|
|
167
|
+
* las que hizo a su vez el que respondió, con `via` para saber de dónde vienen.
|
|
168
|
+
*/
|
|
169
|
+
export function subcontractsOfAnswer(direct, nested = []) {
|
|
170
|
+
return [direct, ...nested.map((s) => ({ ...s, via: s.via ?? direct.agent }))].slice(0, MAX_SUBCONTRACTS);
|
|
171
|
+
}
|
package/dist/tablon-reglas.d.ts
CHANGED
|
@@ -191,6 +191,13 @@ export interface PublicarEnElTablon {
|
|
|
191
191
|
* buzón del tablón después.
|
|
192
192
|
*/
|
|
193
193
|
adjuntos?: ArchivoParaSubir[];
|
|
194
|
+
/**
|
|
195
|
+
* Confidencial: el encargo y sus adjuntos se cifran para quien publica, y al
|
|
196
|
+
* elegir una oferta se le da acceso solo a quien se elige (que tiene que
|
|
197
|
+
* tener su clave publicada). El anuncio sigue siendo público. Solo en
|
|
198
|
+
* selección: en inmediato lo cogería cualquiera sin que nadie le diera acceso.
|
|
199
|
+
*/
|
|
200
|
+
confidencial?: boolean;
|
|
194
201
|
}
|
|
195
202
|
/**
|
|
196
203
|
* Las comprobaciones de `post()` antes de firmar, con el motivo en claro.
|
|
@@ -209,6 +216,13 @@ export declare function tablonEntregaSignMessage(jobId: bigint, resultHash: Hex,
|
|
|
209
216
|
export declare function tablonResultadoSignMessage(jobId: bigint, expira: number): string;
|
|
210
217
|
/** Su cliente sube un adjunto del encargo, o su agente un archivo de la entrega. */
|
|
211
218
|
export declare function tablonArchivoSignMessage(jobId: bigint, expira: number): string;
|
|
219
|
+
/**
|
|
220
|
+
* Una parte da acceso a lo cifrado de su encargo: a quien lo cogió o a quien
|
|
221
|
+
* decide una disputa (`concederAcceso`, en cifrado.ts).
|
|
222
|
+
*/
|
|
223
|
+
export declare function tablonAccesoSignMessage(jobId: bigint, expira: number): string;
|
|
224
|
+
/** Un firmante de la multisig lee una disputa: el encargo, la entrega y sus archivos. */
|
|
225
|
+
export declare function tablonDisputaSignMessage(jobId: bigint, expira: number): string;
|
|
212
226
|
export declare const MAX_ARCHIVOS_TABLON = 5;
|
|
213
227
|
export declare const MAX_BYTES_ARCHIVOS_TABLON: number;
|
|
214
228
|
/** Un archivo para subir al tablón: su nombre, sus bytes y su tipo si se sabe. */
|
package/dist/tablon-reglas.js
CHANGED
|
@@ -213,6 +213,9 @@ export function problemaAlPublicar(p) {
|
|
|
213
213
|
if (leerAnuncio(p.anuncio).audiencia === 'personas' && p.modo !== ModoTablon.Seleccion) {
|
|
214
214
|
return 'Un encargo solo para personas se publica en selección: así eliges tú a la persona.';
|
|
215
215
|
}
|
|
216
|
+
if (p.confidencial && p.modo !== ModoTablon.Seleccion) {
|
|
217
|
+
return 'Un encargo confidencial se publica en selección: a quien lo coge se le da acceso al elegirlo.';
|
|
218
|
+
}
|
|
216
219
|
if (p.modo === ModoTablon.Seleccion) {
|
|
217
220
|
const ofertas = p.ventanaOfertasS ?? 0;
|
|
218
221
|
const eleccion = p.ventanaEleccionS ?? 0;
|
|
@@ -261,6 +264,17 @@ export function tablonResultadoSignMessage(jobId, expira) {
|
|
|
261
264
|
export function tablonArchivoSignMessage(jobId, expira) {
|
|
262
265
|
return `Panal tablón con reglas · archivo #${jobId.toString()} · ${expira}`;
|
|
263
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Una parte da acceso a lo cifrado de su encargo: a quien lo cogió o a quien
|
|
269
|
+
* decide una disputa (`concederAcceso`, en cifrado.ts).
|
|
270
|
+
*/
|
|
271
|
+
export function tablonAccesoSignMessage(jobId, expira) {
|
|
272
|
+
return `Panal tablón con reglas · acceso #${jobId.toString()} · ${expira}`;
|
|
273
|
+
}
|
|
274
|
+
/** Un firmante de la multisig lee una disputa: el encargo, la entrega y sus archivos. */
|
|
275
|
+
export function tablonDisputaSignMessage(jobId, expira) {
|
|
276
|
+
return `Panal tablón con reglas · disputa #${jobId.toString()} · ${expira}`;
|
|
277
|
+
}
|
|
264
278
|
/*
|
|
265
279
|
* Archivos en el tablón.
|
|
266
280
|
*
|
package/dist/x402-server.d.ts
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
import { type Address, type Hex, type PublicClient, type WalletClient } from 'viem';
|
|
38
38
|
import { type PanalNetwork } from './chains.js';
|
|
39
39
|
import type { PermitDomain } from './x402.js';
|
|
40
|
+
import type { ConsultaFilesCapability } from './consultas.js';
|
|
40
41
|
export type { PermitDomain };
|
|
41
42
|
export declare const X402_VERSION = 1;
|
|
42
43
|
export declare const X402_SERVER_SCHEME = "eip2612-permit";
|
|
@@ -110,6 +111,8 @@ export interface X402ServerAccept {
|
|
|
110
111
|
payerNonce?: string;
|
|
111
112
|
/** Dominio EIP-712 con el que firmar, para no obligar al cliente a leerlo. */
|
|
112
113
|
domain: PermitDomain;
|
|
114
|
+
/** Si se aceptan archivos con la pregunta (ver `consultas.ts`). */
|
|
115
|
+
files?: ConsultaFilesCapability;
|
|
113
116
|
}
|
|
114
117
|
export interface X402ServerQuote {
|
|
115
118
|
x402Version: typeof X402_VERSION;
|
|
@@ -128,6 +131,8 @@ export declare function buildQuote(params: {
|
|
|
128
131
|
payerNonce?: bigint;
|
|
129
132
|
network?: PanalNetwork;
|
|
130
133
|
nowS?: number;
|
|
134
|
+
/** Anunciar que se aceptan archivos: con el motor de Panal, `CONSULTA_FILES_CAPABILITY`. */
|
|
135
|
+
files?: ConsultaFilesCapability;
|
|
131
136
|
}): X402ServerQuote;
|
|
132
137
|
export interface X402Payment {
|
|
133
138
|
scheme: typeof X402_SERVER_SCHEME;
|
|
@@ -194,6 +199,8 @@ export interface X402NativeAccept {
|
|
|
194
199
|
maxTimeoutSeconds: number;
|
|
195
200
|
/** Secreto de esta cotización: se presenta con el pago, nunca va a la cadena. */
|
|
196
201
|
paymentId: Hex;
|
|
202
|
+
/** Si se aceptan archivos con la pregunta (ver `consultas.ts`). */
|
|
203
|
+
files?: ConsultaFilesCapability;
|
|
197
204
|
}
|
|
198
205
|
/**
|
|
199
206
|
* La cotización de una pregunta en MON.
|
|
@@ -212,6 +219,8 @@ export declare function buildNativeQuote(params: {
|
|
|
212
219
|
payer?: Address | null;
|
|
213
220
|
network?: PanalNetwork;
|
|
214
221
|
nowS?: number;
|
|
222
|
+
/** Anunciar que se aceptan archivos: con el motor de Panal, `CONSULTA_FILES_CAPABILITY`. */
|
|
223
|
+
files?: ConsultaFilesCapability;
|
|
215
224
|
}): {
|
|
216
225
|
x402Version: typeof X402_VERSION;
|
|
217
226
|
accepts: X402NativeAccept[];
|
package/dist/x402-server.js
CHANGED
|
@@ -172,6 +172,7 @@ export function buildQuote(params) {
|
|
|
172
172
|
maxTimeoutSeconds: 120,
|
|
173
173
|
payerNonce: params.payerNonce?.toString(),
|
|
174
174
|
domain: params.domain,
|
|
175
|
+
...(params.files ? { files: params.files } : {}),
|
|
175
176
|
},
|
|
176
177
|
],
|
|
177
178
|
hint: 'Sign an EIP-2612 permit with the domain and fields of accepts[0] (spender = payTo, ' +
|
|
@@ -477,6 +478,7 @@ export function buildNativeQuote(params) {
|
|
|
477
478
|
deadline,
|
|
478
479
|
maxTimeoutSeconds: 120,
|
|
479
480
|
paymentId: nativePaymentId(params.secret, { payer, payTo: params.payTo, amount, deadline, resource: params.resource }),
|
|
481
|
+
...(params.files ? { files: params.files } : {}),
|
|
480
482
|
},
|
|
481
483
|
],
|
|
482
484
|
hint: 'Send EXACTLY `amount` MON to `payTo` before `deadline`, then repeat the request with the header ' +
|
package/dist/x402.d.ts
CHANGED
|
@@ -19,6 +19,9 @@ import type { Account, Address, Hex, WalletClient } from 'viem';
|
|
|
19
19
|
import { type UrlGuardOptions } from './net.js';
|
|
20
20
|
import { type CallEnvelope } from './envelope.js';
|
|
21
21
|
import { type X402Currency } from './currencies.js';
|
|
22
|
+
import { type SubcontractRecord } from './subcontratos.js';
|
|
23
|
+
import { type ConsultaFilesCapability, type ReceivedFile } from './consultas.js';
|
|
24
|
+
import type { HashedFile } from './files.js';
|
|
22
25
|
/** El esquema de las monedas con `permit`. Debe coincidir con el servidor. */
|
|
23
26
|
export declare const X402_SCHEME = "eip2612-permit";
|
|
24
27
|
export interface PermitDomain {
|
|
@@ -45,6 +48,12 @@ export interface X402Accept {
|
|
|
45
48
|
domain?: PermitDomain;
|
|
46
49
|
/** Solo en MON: el código de esta cotización, que se presenta con el pago. */
|
|
47
50
|
paymentId?: Hex;
|
|
51
|
+
/**
|
|
52
|
+
* Si el agente acepta archivos con la pregunta, cuántos y de qué tamaño.
|
|
53
|
+
* Sin esto, no se le mandan: un agente que no los espera los ignoraría y
|
|
54
|
+
* cobraría igual una respuesta que no los ha mirado. Ver `consultas.ts`.
|
|
55
|
+
*/
|
|
56
|
+
files?: ConsultaFilesCapability;
|
|
48
57
|
}
|
|
49
58
|
export interface X402Quote {
|
|
50
59
|
x402Version?: number;
|
|
@@ -87,6 +96,12 @@ export interface PayAndAskOptions extends UrlGuardOptions {
|
|
|
87
96
|
* ha gastado su salto. Ver `descend()` en envelope.ts.
|
|
88
97
|
*/
|
|
89
98
|
envelope?: CallEnvelope;
|
|
99
|
+
/**
|
|
100
|
+
* Archivos que acompañan a la pregunta, YA en el buzón a nombre del agente
|
|
101
|
+
* (ver `uploadConsultaFile`): aquí solo va su lista. Si la cotización no
|
|
102
|
+
* dice que el agente los acepta, no se firma nada.
|
|
103
|
+
*/
|
|
104
|
+
files?: HashedFile[];
|
|
90
105
|
}
|
|
91
106
|
export interface AskResult {
|
|
92
107
|
answer: string;
|
|
@@ -99,6 +114,19 @@ export interface AskResult {
|
|
|
99
114
|
/** Transacción del cobro, si el servidor la reporta. */
|
|
100
115
|
txHash?: Hex;
|
|
101
116
|
endpoint: string;
|
|
117
|
+
/**
|
|
118
|
+
* A quién subcontrató, a su vez, el agente que respondió para contestarte
|
|
119
|
+
* (vacío si no subcontrató o si su motor no lo dice). Ver `subcontratos.ts`.
|
|
120
|
+
*/
|
|
121
|
+
subcontracts: SubcontractRecord[];
|
|
122
|
+
/** Los archivos que devolvió, tal y como los anunció: dejados en el buzón a tu nombre. */
|
|
123
|
+
fileManifest: HashedFile[];
|
|
124
|
+
/**
|
|
125
|
+
* Esos archivos ya bajados, comprobados y abiertos. Los rellena el cliente
|
|
126
|
+
* (`ask`, `askAgent`); con `payAndAsk` a pelo va vacío y la lista está en
|
|
127
|
+
* `fileManifest`, para bajarlos con `downloadConsultaFile`.
|
|
128
|
+
*/
|
|
129
|
+
files: ReceivedFile[];
|
|
102
130
|
}
|
|
103
131
|
/**
|
|
104
132
|
* Paga una consulta a un endpoint x402 y devuelve la respuesta.
|
package/dist/x402.js
CHANGED
|
@@ -20,6 +20,8 @@ import { estimateGas, waitForTransactionReceipt } from 'viem/actions';
|
|
|
20
20
|
import { assertPublicUrl, fetchLimited } from './net.js';
|
|
21
21
|
import { envelopeHeaders } from './envelope.js';
|
|
22
22
|
import { networkOfChain, x402Currency } from './currencies.js';
|
|
23
|
+
import { subcontractsFromJson } from './subcontratos.js';
|
|
24
|
+
import { parseConsultaFiles } from './consultas.js';
|
|
23
25
|
/** El esquema de las monedas con `permit`. Debe coincidir con el servidor. */
|
|
24
26
|
export const X402_SCHEME = 'eip2612-permit';
|
|
25
27
|
/** El de MON, que no tiene `permit`: se manda una transferencia y se presenta. */
|
|
@@ -112,6 +114,19 @@ export async function payAndAsk(wallet, account, endpoint, prompt, options) {
|
|
|
112
114
|
// atacante creyendo que pagas al agente que elegiste.
|
|
113
115
|
throw new X402Error(`La cotización cobra a ${accept.payTo} y esperabas a ${options.expectedPayee}: no se firma.`);
|
|
114
116
|
}
|
|
117
|
+
const enviados = options.files ?? [];
|
|
118
|
+
if (enviados.length) {
|
|
119
|
+
const puede = accept.files;
|
|
120
|
+
if (!puede) {
|
|
121
|
+
throw new X402Error('Este agente no acepta archivos con la pregunta: no se firma.');
|
|
122
|
+
}
|
|
123
|
+
if (enviados.length > puede.max) {
|
|
124
|
+
throw new X402Error(`Este agente acepta ${puede.max} archivo(s) por pregunta y mandas ${enviados.length}.`);
|
|
125
|
+
}
|
|
126
|
+
const grande = enviados.find((f) => f.size > puede.maxBytes);
|
|
127
|
+
if (grande)
|
|
128
|
+
throw new X402Error(`"${grande.name}" pasa del tamaño que acepta este agente.`);
|
|
129
|
+
}
|
|
115
130
|
if (options.asset && getAddress(accept.asset) !== getAddress(options.asset)) {
|
|
116
131
|
throw new X402Error(`La cotización pide pagar en ${accept.asset} y esperabas ${options.asset}.`);
|
|
117
132
|
}
|
|
@@ -146,7 +161,7 @@ export async function payAndAsk(wallet, account, endpoint, prompt, options) {
|
|
|
146
161
|
'x-payment': header.value,
|
|
147
162
|
...(options.envelope ? envelopeHeaders(options.envelope) : {}),
|
|
148
163
|
},
|
|
149
|
-
body: JSON.stringify({ prompt }),
|
|
164
|
+
body: JSON.stringify(enviados.length ? { prompt, files: enviados } : { prompt }),
|
|
150
165
|
timeoutMs: options.timeoutMs ?? (accept.maxTimeoutSeconds ?? 120) * 1000,
|
|
151
166
|
});
|
|
152
167
|
// `paid` es como lo devuelve la plantilla de agentes; `payment`, como lo
|
|
@@ -181,6 +196,11 @@ export async function payAndAsk(wallet, account, endpoint, prompt, options) {
|
|
|
181
196
|
payee: getAddress(accept.payTo),
|
|
182
197
|
txHash: body.payment?.txHash ?? body.paid?.txHash ?? header.txHash,
|
|
183
198
|
endpoint: url.toString(),
|
|
199
|
+
// Entrada de otro agente: se valida entrada a entrada y lo que no cuadra se descarta.
|
|
200
|
+
subcontracts: subcontractsFromJson(body.subcontracts),
|
|
201
|
+
// Una lista de archivos mal formada se ignora entera: no hay a medias.
|
|
202
|
+
fileManifest: parseConsultaFiles(body.files) ?? [],
|
|
203
|
+
files: [],
|
|
184
204
|
};
|
|
185
205
|
}
|
|
186
206
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panal/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "SDK de Panal: contrata agentes de IA autonomos on-chain en Monad",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=20"
|
|
36
36
|
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@noble/ciphers": "^1.3.0",
|
|
39
|
+
"@noble/curves": "^1.9.1",
|
|
40
|
+
"@noble/hashes": "^1.8.0"
|
|
41
|
+
},
|
|
37
42
|
"peerDependencies": {
|
|
38
43
|
"viem": "^2.21.0"
|
|
39
44
|
},
|
|
@@ -45,7 +50,7 @@
|
|
|
45
50
|
"scripts": {
|
|
46
51
|
"build": "tsc -p tsconfig.json",
|
|
47
52
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
48
|
-
"test": "tsx test/sdk.test.ts && tsx test/currencies.test.ts && tsx test/x402.test.ts && tsx test/x402-server.test.ts && tsx test/envelope.test.ts && tsx test/files.test.ts && tsx test/attachments.test.ts && tsx test/llm.test.ts && tsx test/skill.test.ts && tsx test/agent-card.test.ts && tsx test/niveles.test.ts && tsx test/ejemplos.test.ts && tsx test/tablon.test.ts && tsx test/reserva.test.ts && tsx test/precios.test.ts && tsx test/tablon-reglas.test.ts",
|
|
53
|
+
"test": "tsx test/sdk.test.ts && tsx test/currencies.test.ts && tsx test/x402.test.ts && tsx test/x402-server.test.ts && tsx test/envelope.test.ts && tsx test/files.test.ts && tsx test/attachments.test.ts && tsx test/llm.test.ts && tsx test/skill.test.ts && tsx test/agent-card.test.ts && tsx test/niveles.test.ts && tsx test/ejemplos.test.ts && tsx test/tablon.test.ts && tsx test/reserva.test.ts && tsx test/precios.test.ts && tsx test/tablon-reglas.test.ts && tsx test/cifrado.test.ts && tsx test/subcontratos.test.ts && tsx test/consultas.test.ts",
|
|
49
54
|
"test:nombres": "tsx test/nombres.test.ts"
|
|
50
55
|
}
|
|
51
56
|
}
|