create-panal-agent 0.7.0 → 0.8.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 +2 -2
- package/template/src/agent.ts +9 -1
- package/template/src/register.ts +129 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-panal-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"starter"
|
|
20
20
|
],
|
|
21
21
|
"bin": {
|
|
22
|
-
"create-panal-agent": "
|
|
22
|
+
"create-panal-agent": "dist/index.js"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
package/template/src/agent.ts
CHANGED
|
@@ -339,7 +339,15 @@ async function pedirAlModelo(
|
|
|
339
339
|
// 3. Sin la del registro, envuelve el trabajo en "¡Claro! Aquí
|
|
340
340
|
// tienes…" y el entregable parece un chat, no un producto.
|
|
341
341
|
'You are a professional agent on the Panal marketplace. ' +
|
|
342
|
-
|
|
342
|
+
// El "never fall back to English" y lo de los títulos no son
|
|
343
|
+
// adorno: en producción, una petición en portugués volvió entera en
|
|
344
|
+
// inglés porque el prompt listaba los títulos de sección en inglés y
|
|
345
|
+
// el modelo los copiaba; y otra en chino devolvió las claves en
|
|
346
|
+
// inglés. Los dos fallos con la regla del idioma ya puesta.
|
|
347
|
+
'RULE 1, before anything else: detect the language of the request and reply in that exact same ' +
|
|
348
|
+
'language; never switch part-way, and never fall back to English because the request is not in ' +
|
|
349
|
+
'English. If the instructions below name sections, headings or field names, translate those too: ' +
|
|
350
|
+
'they are written in one language only because these instructions are. ' +
|
|
343
351
|
'RULE 2: plain text only, never Markdown — no # headings, no ** bold, no backticks. ' +
|
|
344
352
|
'RULE 3: deliver finished professional work, with no preamble or meta-commentary.\n' +
|
|
345
353
|
// El agente adjunta el archivo por su cuenta; el modelo no se entera
|
package/template/src/register.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import 'dotenv/config';
|
|
15
15
|
import { createPanalClient, formatAgentMetadata, NATIVE_CURRENCY } from '@panal/sdk';
|
|
16
16
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
17
|
-
import { formatEther, parseEther } from 'viem';
|
|
17
|
+
import { createPublicClient, createWalletClient, formatEther, http, parseEther } from 'viem';
|
|
18
18
|
|
|
19
19
|
// ────────────────────────────────────────────────────────────────────────────
|
|
20
20
|
// RELLENA ESTO. Es tu escaparate: lo que verá quien busque un agente.
|
|
@@ -214,10 +214,138 @@ async function main(): Promise<void> {
|
|
|
214
214
|
console.log('Registrado.');
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
// El nombre va DESPUÉS del registro y no puede tumbarlo: `reclamar` exige
|
|
218
|
+
// estar registrado y activo, así que el orden es obligatorio, y si algo falla
|
|
219
|
+
// —nombre cogido, sin saldo, contrato no desplegado— el agente ya está
|
|
220
|
+
// trabajando igual. El nombre es un extra, no un requisito.
|
|
221
|
+
await reclamaTuNombre(account, PERFIL.name);
|
|
222
|
+
|
|
217
223
|
console.log(`\nYa apareces en https://panal.lat/market`);
|
|
218
224
|
console.log(`Compruébalo desde Claude: "¿qué agentes hay en Panal?"`);
|
|
219
225
|
}
|
|
220
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Convierte el nombre del perfil en un handle válido para PanalNames.
|
|
229
|
+
*
|
|
230
|
+
* El contrato solo acepta `a-z`, `0-9` y `-`, y ahí es donde mueren los
|
|
231
|
+
* homoglifos: la `а` cirílica no colisiona con la latina, es que no se puede
|
|
232
|
+
* escribir. Así que "LexPanal" pasa a `lexpanal` y "Traductor ES→DE" a
|
|
233
|
+
* `traductor-es-de`.
|
|
234
|
+
*
|
|
235
|
+
* Los acentos se quitan descomponiendo el texto (NFD) y tirando las marcas:
|
|
236
|
+
* "Ágil" -> `agil`. Transliterar a ojo cada idioma sería inventar.
|
|
237
|
+
*/
|
|
238
|
+
export function aHandle(nombre: string): string {
|
|
239
|
+
return nombre
|
|
240
|
+
.normalize('NFD')
|
|
241
|
+
.replace(/\p{M}/gu, '')
|
|
242
|
+
.toLowerCase()
|
|
243
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
244
|
+
.replace(/^-+|-+$/g, '')
|
|
245
|
+
.slice(0, 32)
|
|
246
|
+
.replace(/-+$/, '');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* PanalNames en Monad mainnet. Vacío mientras no esté desplegado, y entonces
|
|
251
|
+
* este paso no hace nada — que es lo correcto: no hay contrato al que pedirle.
|
|
252
|
+
* Se puede apuntar a otro con PANAL_NAMES_ADDRESS.
|
|
253
|
+
*/
|
|
254
|
+
const PANAL_NAMES = '';
|
|
255
|
+
|
|
256
|
+
const NOMBRES_ABI = [
|
|
257
|
+
{
|
|
258
|
+
type: 'function',
|
|
259
|
+
name: 'disponible',
|
|
260
|
+
stateMutability: 'view',
|
|
261
|
+
inputs: [{ name: 'nombre', type: 'string' }],
|
|
262
|
+
outputs: [{ name: '', type: 'bool' }],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
type: 'function',
|
|
266
|
+
name: 'nombreDe',
|
|
267
|
+
stateMutability: 'view',
|
|
268
|
+
inputs: [{ name: 'agente', type: 'address' }],
|
|
269
|
+
outputs: [{ name: '', type: 'string' }],
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
type: 'function',
|
|
273
|
+
name: 'tarifaDe',
|
|
274
|
+
stateMutability: 'view',
|
|
275
|
+
inputs: [{ name: 'nombre', type: 'string' }],
|
|
276
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
type: 'function',
|
|
280
|
+
name: 'reclamar',
|
|
281
|
+
stateMutability: 'nonpayable',
|
|
282
|
+
inputs: [{ name: 'nombre', type: 'string' }],
|
|
283
|
+
outputs: [],
|
|
284
|
+
},
|
|
285
|
+
] as const;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Reclama tu nombre único en PanalNames, si se puede.
|
|
289
|
+
*
|
|
290
|
+
* NUNCA lanza. Todo lo que puede salir mal aquí —que el contrato no esté
|
|
291
|
+
* desplegado, que el nombre esté cogido, que no tengas saldo— es un extra que
|
|
292
|
+
* no sale, y el agente ya está registrado y trabajando. Se avisa y se sigue.
|
|
293
|
+
*
|
|
294
|
+
* Se hace en el mismo comando a propósito: si hay que volver días después a
|
|
295
|
+
* reclamarlo, para entonces se lo habrá quedado otro.
|
|
296
|
+
*/
|
|
297
|
+
async function reclamaTuNombre(account: ReturnType<typeof privateKeyToAccount>, nombre: string): Promise<void> {
|
|
298
|
+
const contrato = process.env.PANAL_NAMES_ADDRESS?.trim() || PANAL_NAMES;
|
|
299
|
+
if (!contrato || !/^0x[0-9a-fA-F]{40}$/.test(contrato)) return;
|
|
300
|
+
|
|
301
|
+
const handle = aHandle(nombre);
|
|
302
|
+
if (handle.length < 3) {
|
|
303
|
+
// Pasa con los nombres en alfabetos no latinos: el contrato solo acepta
|
|
304
|
+
// `a-z0-9-`, que es lo que impide los homoglifos, así que de "日本語" no
|
|
305
|
+
// sale nada. No es un fallo, pero hay que decir qué hacer.
|
|
306
|
+
console.log(`\nNo te reclamo nombre: de "${nombre}" no sale un handle de 3 letras o más.`);
|
|
307
|
+
console.log(`Los nombres solo admiten a-z, 0-9 y guion. Elige uno a mano desde https://panal.lat/dashboard`);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const rpc = process.env.RPC_URL?.trim() || 'https://rpc.monad.xyz';
|
|
313
|
+
const chain = { id: 143, name: 'Monad', nativeCurrency: { name: 'MON', symbol: 'MON', decimals: 18 }, rpcUrls: { default: { http: [rpc] } } } as const;
|
|
314
|
+
const publico = createPublicClient({ transport: http(rpc) });
|
|
315
|
+
const cartera = createWalletClient({ account, chain, transport: http(rpc) });
|
|
316
|
+
const donde = contrato as `0x${string}`;
|
|
317
|
+
|
|
318
|
+
const yaTengo = await publico.readContract({ address: donde, abi: NOMBRES_ABI, functionName: 'nombreDe', args: [account.address] });
|
|
319
|
+
if (yaTengo) {
|
|
320
|
+
console.log(`\nTu nombre en Panal ya es: ${yaTengo}`);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const libre = await publico.readContract({ address: donde, abi: NOMBRES_ABI, functionName: 'disponible', args: [handle] });
|
|
325
|
+
if (!libre) {
|
|
326
|
+
console.log(`\nEl nombre "${handle}" ya está cogido. Puedes reclamar otro desde https://panal.lat/dashboard`);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const tarifa = await publico.readContract({ address: donde, abi: NOMBRES_ABI, functionName: 'tarifaDe', args: [handle] });
|
|
331
|
+
if (tarifa > 0n) {
|
|
332
|
+
// Con tarifa hay que aprobar el gasto antes, y eso es otra firma y otra
|
|
333
|
+
// decision. No se hace a tus espaldas: se te dice y lo haces tu.
|
|
334
|
+
console.log(`\nTu nombre "${handle}" está libre, pero cuesta ${formatEther(tarifa)} $PANAL.`);
|
|
335
|
+
console.log(`Reclámalo desde https://panal.lat/dashboard cuando quieras.`);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const hash = await cartera.writeContract({ address: donde, abi: NOMBRES_ABI, functionName: 'reclamar', args: [handle], chain });
|
|
340
|
+
await publico.waitForTransactionReceipt({ hash });
|
|
341
|
+
console.log(`\nTu nombre único en Panal: ${handle}`);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
// Un fallo aqui no es grave: el agente ya esta registrado y puede trabajar.
|
|
344
|
+
console.log(`\nNo pude reclamarte el nombre (${err instanceof Error ? err.message.split('\n')[0] : err}).`);
|
|
345
|
+
console.log(`Puedes hacerlo luego desde https://panal.lat/dashboard`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
221
349
|
main().catch((err) => {
|
|
222
350
|
console.error(`\nFalló: ${err instanceof Error ? err.message : err}`);
|
|
223
351
|
process.exit(1);
|