@pasosdejesus/mpdj 0.1.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.
Files changed (103) hide show
  1. package/LICENSE.md +16 -0
  2. package/SKILL.md +310 -0
  3. package/dist/SKILL.md +310 -0
  4. package/dist/abis/PasosDeJesusCredentials.json +1342 -0
  5. package/dist/blockchain/AddSlearnButton.d.ts +5 -0
  6. package/dist/blockchain/AddSlearnButton.js +58 -0
  7. package/dist/blockchain/SlearnInfo.d.ts +22 -0
  8. package/dist/blockchain/SlearnInfo.js +68 -0
  9. package/dist/blockchain/admin-credentials.d.ts +29 -0
  10. package/dist/blockchain/admin-credentials.js +91 -0
  11. package/dist/blockchain/credential-cache.d.ts +33 -0
  12. package/dist/blockchain/credential-cache.js +129 -0
  13. package/dist/blockchain/credential-images.d.ts +30 -0
  14. package/dist/blockchain/credential-images.js +199 -0
  15. package/dist/blockchain/credentials.d.ts +66 -0
  16. package/dist/blockchain/credentials.js +128 -0
  17. package/dist/blockchain/ecosystem-addresses.d.ts +9 -0
  18. package/dist/blockchain/ecosystem-addresses.js +23 -0
  19. package/dist/blockchain/index.d.ts +6 -0
  20. package/dist/blockchain/index.js +12 -0
  21. package/dist/ia/adJ.md +181 -0
  22. package/dist/ia/alineacion_bayes_general.md +60 -0
  23. package/dist/ia/alineacion_bayes_profecia_mayor20G.md +220 -0
  24. package/dist/ia/alineacion_servicio.md +49 -0
  25. package/dist/ia/alineacion_testimonios.md +268 -0
  26. package/dist/ia/construir_en_adJ_y_celo.md +239 -0
  27. package/dist/ia/entrenamiento-marcos.jsonl +23 -0
  28. package/dist/ia/entrenamiento-no-adivinar.jsonl +15 -0
  29. package/dist/ia/estilo_entrenamiento.jsonl +10 -0
  30. package/dist/ia/indice_entrenamiento.json +43 -0
  31. package/dist/ia/m.jsonl +3 -0
  32. package/dist/ia/manifiesto.md +45 -0
  33. package/dist/ia/piedra_fundamental/README.md +31 -0
  34. package/dist/ia/piedra_fundamental/marcos-01.usfm +68 -0
  35. package/dist/ia/piedra_fundamental/marcos-02.usfm +46 -0
  36. package/dist/ia/piedra_fundamental/marcos-03.usfm +48 -0
  37. package/dist/ia/piedra_fundamental/marcos-04.usfm +59 -0
  38. package/dist/ia/piedra_fundamental/marcos-05.usfm +65 -0
  39. package/dist/ia/piedra_fundamental/marcos-06.usfm +78 -0
  40. package/dist/ia/piedra_fundamental/marcos-07.usfm +53 -0
  41. package/dist/ia/piedra_fundamental/marcos-08.usfm +63 -0
  42. package/dist/ia/piedra_fundamental/marcos-09.usfm +82 -0
  43. package/dist/ia/piedra_fundamental/marcos-10.usfm +88 -0
  44. package/dist/ia/piedra_fundamental/marcos-11.usfm +52 -0
  45. package/dist/ia/piedra_fundamental/marcos-12.usfm +62 -0
  46. package/dist/ia/piedra_fundamental/marcos-13.usfm +48 -0
  47. package/dist/ia/piedra_fundamental/marcos-14.usfm +108 -0
  48. package/dist/ia/piedra_fundamental/marcos-15.usfm +70 -0
  49. package/dist/ia/piedra_fundamental/marcos-16.usfm +32 -0
  50. package/dist/ia/principles.md +35 -0
  51. package/dist/ia/referencia-marcos.md +152 -0
  52. package/dist/ia/verificacion_cristologica.json +128 -0
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.js +159 -0
  55. package/dist/tasks/adJ.d.ts +1 -0
  56. package/dist/tasks/adJ.js +37 -0
  57. package/dist/tasks/ai-verify-core.d.ts +27 -0
  58. package/dist/tasks/ai-verify-core.js +82 -0
  59. package/dist/tasks/ai.d.ts +3 -0
  60. package/dist/tasks/ai.js +96 -0
  61. package/dist/tasks/credentials.d.ts +4 -0
  62. package/dist/tasks/credentials.js +627 -0
  63. package/dist/tasks/principles.d.ts +1 -0
  64. package/dist/tasks/principles.js +37 -0
  65. package/dist/tasks/req.d.ts +9 -0
  66. package/dist/tasks/req.js +1164 -0
  67. package/dist/tasks/skill.d.ts +1 -0
  68. package/dist/tasks/skill.js +34 -0
  69. package/dist/test-utils/blockchain-mocks.d.ts +130 -0
  70. package/dist/test-utils/blockchain-mocks.js +112 -0
  71. package/ia/adJ.md +181 -0
  72. package/ia/alineacion_bayes_general.md +60 -0
  73. package/ia/alineacion_bayes_profecia_mayor20G.md +220 -0
  74. package/ia/alineacion_servicio.md +49 -0
  75. package/ia/alineacion_testimonios.md +268 -0
  76. package/ia/construir_en_adJ_y_celo.md +239 -0
  77. package/ia/entrenamiento-marcos.jsonl +23 -0
  78. package/ia/entrenamiento-no-adivinar.jsonl +15 -0
  79. package/ia/estilo_entrenamiento.jsonl +10 -0
  80. package/ia/indice_entrenamiento.json +43 -0
  81. package/ia/m.jsonl +3 -0
  82. package/ia/manifiesto.md +45 -0
  83. package/ia/piedra_fundamental/README.md +31 -0
  84. package/ia/piedra_fundamental/marcos-01.usfm +68 -0
  85. package/ia/piedra_fundamental/marcos-02.usfm +46 -0
  86. package/ia/piedra_fundamental/marcos-03.usfm +48 -0
  87. package/ia/piedra_fundamental/marcos-04.usfm +59 -0
  88. package/ia/piedra_fundamental/marcos-05.usfm +65 -0
  89. package/ia/piedra_fundamental/marcos-06.usfm +78 -0
  90. package/ia/piedra_fundamental/marcos-07.usfm +53 -0
  91. package/ia/piedra_fundamental/marcos-08.usfm +63 -0
  92. package/ia/piedra_fundamental/marcos-09.usfm +82 -0
  93. package/ia/piedra_fundamental/marcos-10.usfm +88 -0
  94. package/ia/piedra_fundamental/marcos-11.usfm +52 -0
  95. package/ia/piedra_fundamental/marcos-12.usfm +62 -0
  96. package/ia/piedra_fundamental/marcos-13.usfm +48 -0
  97. package/ia/piedra_fundamental/marcos-14.usfm +108 -0
  98. package/ia/piedra_fundamental/marcos-15.usfm +70 -0
  99. package/ia/piedra_fundamental/marcos-16.usfm +32 -0
  100. package/ia/principles.md +35 -0
  101. package/ia/referencia-marcos.md +152 -0
  102. package/ia/verificacion_cristologica.json +128 -0
  103. package/package.json +73 -0
@@ -0,0 +1,627 @@
1
+ // src/tasks/credentials.ts
2
+ // CLI commands for PasosDeJesusCredentials administration.
3
+ //
4
+ // Commands:
5
+ // m credentials:list-types --network <net>
6
+ // m credentials:mint --network <net> --token-id <id> --address <wallet> [--amount <n>]
7
+ // m credentials:register-type --network <net> --site <name> --type <name> --display <name>
8
+ // --soulbound <true|false> [--course-id <id>] [--premium]
9
+ // [--custom-uri <uri>] [--max-supply <n>]
10
+ // [--icon public/img/credential/source/myicon.svg]
11
+ // m credentials:recompose-image --token-id <id> --network <net> --icon <path>
12
+ // m credentials:set-max-supply --network <net> --token-id <id> --max <n>
13
+ // m credentials:set-site-uri --network <net> --site <name> --uri <url>
14
+ // m credentials:sync-cache --network <net>
15
+ // m credentials:grant-minter --network <net> --address <wallet>
16
+ // m credentials:revoke-minter --network <net> --address <wallet>
17
+ // m credentials:revoke-credential --network <net> --token-id <id> --address <wallet> [--amount <n>]
18
+ //
19
+ // Conditional visibility:
20
+ // - If apps/hardhat/deployments/PasosDeJesusCredentials/ exists → credentials:* appears in CLI
21
+ // - If CREDENTIALS_PRIVATE_KEY is set → admin commands (grant-minter, revoke-minter) available
22
+ import * as fs from 'fs';
23
+ import * as path from 'path';
24
+ import { createRequire } from 'module';
25
+ import { problem } from '@pasosdejesus/m/dist/util/colors.js';
26
+ import { createTranslator } from '@pasosdejesus/m/dist/i18n/createTranslator.js';
27
+ import { detectLocale } from '@pasosdejesus/m/dist/util/locale.js';
28
+ import commonCli from '@pasosdejesus/m/dist/i18n/common-cli.js';
29
+ const require = createRequire(import.meta.url);
30
+ const locale = detectLocale();
31
+ const ct = createTranslator(locale, {
32
+ en: {
33
+ 'cred.usage_header': 'Usage: m credentials:<command> [args]',
34
+ 'cred.usage_commands': 'Commands:',
35
+ 'cred.usage_list_types': 'list-types --network <net>',
36
+ 'cred.usage_mint': 'mint --network <net> --token-id <id> --address <wallet> [--amount <n>]',
37
+ 'cred.usage_register': 'register-type --network <net> --site <name> --type <name> --display <name>',
38
+ 'cred.usage_register_opts': ' --soulbound <true|false> [--course-id <id>] [--premium]',
39
+ 'cred.usage_register_icon': ' [--icon public/img/credential/source/myicon.svg]',
40
+ 'cred.usage_recompose': 'recompose-image --token-id <id> --network <net> --icon <path>',
41
+ 'cred.usage_set_max': 'set-max-supply --network <net> --token-id <id> --max <n>',
42
+ 'cred.usage_set_uri': 'set-site-uri --network <net> --site <name> --uri <url>',
43
+ 'cred.usage_sync': 'sync-cache --network <net>',
44
+ 'cred.usage_grant': 'grant-minter --network <net> --address <wallet>',
45
+ 'cred.usage_revoke_minter': 'revoke-minter --network <net> --address <wallet>',
46
+ 'cred.usage_revoke': 'revoke-credential --network <net> --token-id <id> --address <wallet> [--amount <n>]',
47
+ 'cred.usage_networks': 'Networks: celo, celoSepolia, base, baseSepolia',
48
+ 'cred.unknown_command': 'Unknown command: {{0}}',
49
+ 'cred.no_admin_key': 'CREDENTIALS_PRIVATE_KEY required for admin commands',
50
+ 'cred.key_missing': 'PRIVATE_KEY not set',
51
+ 'cred.key_or_admin_missing': 'CREDENTIALS_PRIVATE_KEY not set in .env',
52
+ 'cred.no_privkey': 'PRIVATE_KEY not set',
53
+ 'cred.emission_failed': 'Emission tracking failed: {{0}}',
54
+ 'cred.minted': 'Minted token {{0}} x{{1}} to {{2}} on {{3}}. TX: {{4}}',
55
+ 'cred.types_header': 'Credential types on {{0}} (nextTokenId: {{1}}):',
56
+ 'cred.type_row': ' {{0}}: {{1}} (soulbound: {{2}}, maxSupply: {{3}})',
57
+ 'cred.register_success': 'Credential type registered. tokenId: {{0}} site: {{1}} type: {{2}} name: {{3}}',
58
+ 'cred.cache_failed': 'Cache update failed: {{0}}',
59
+ 'cred.image_failed': 'Image composition failed (tokenId {{0}}): {{1}}',
60
+ 'cred.recompose_title': 'Image recomposed for token {{0}} ({{1}})',
61
+ 'cred.recompose_svg': ' SVG: {{0}}',
62
+ 'cred.recompose_png': ' PNG: {{0}}',
63
+ 'cred.token_not_in_cache': 'Token {{0}} not found in credential_metadata cache. Register first with register-type.',
64
+ 'cred.max_set': 'maxSupply set. tokenId: {{0}} max: {{1}} TX: {{2}}',
65
+ 'cred.uri_set': 'siteBaseURI set. site: {{0}} uri: {{1}} TX: {{2}}',
66
+ 'cred.granted': 'MINTER_ROLE granted to {{0}} on {{1}}. TX: {{2}}',
67
+ 'cred.revoked_minter': 'MINTER_ROLE revoked from {{0}} on {{1}}. TX: {{2}}',
68
+ 'cred.revoked': 'Revoked token {{0}} x{{1}} from {{2}} on {{3}}. TX: {{4}}',
69
+ 'cred.sync_row': ' Cached token {{0}}: {{1}}',
70
+ 'cred.sync_done': 'Sync complete. {{0}} tokens cached on {{1}}.',
71
+ 'cred.deploy_not_found': 'No deployment found for {{0}}. Deploy first or set {{1}} in .env',
72
+ 'cred.unknown_network': 'Unknown network: {{0}}',
73
+ 'cred.args_missing_mint': '--network, --token-id, --address required',
74
+ 'cred.args_missing_register': '--network, --site, --type, --display required',
75
+ 'cred.args_missing_recompose': '--token-id and --icon required',
76
+ 'cred.args_missing_max': '--network, --token-id, --max required',
77
+ 'cred.args_missing_uri': '--network, --site, --uri required',
78
+ 'cred.args_missing_grant': '--network and --address required',
79
+ 'cred.args_missing_revoke': '--network, --token-id, --address required',
80
+ },
81
+ es: {
82
+ 'cred.usage_header': 'Uso: m credentials:<orden> [args]',
83
+ 'cred.usage_commands': 'Órdenes:',
84
+ 'cred.usage_list_types': 'list-types --network <red>',
85
+ 'cred.usage_mint': 'mint --network <red> --token-id <id> --address <billetera> [--amount <n>]',
86
+ 'cred.usage_register': 'register-type --network <red> --site <nombre> --type <nombre> --display <nombre>',
87
+ 'cred.usage_register_opts': ' --soulbound <true|false> [--course-id <id>] [--premium]',
88
+ 'cred.usage_register_icon': ' [--icon public/img/credential/source/miicono.svg]',
89
+ 'cred.usage_recompose': 'recompose-image --token-id <id> --network <red> --icon <ruta>',
90
+ 'cred.usage_set_max': 'set-max-supply --network <red> --token-id <id> --max <n>',
91
+ 'cred.usage_set_uri': 'set-site-uri --network <red> --site <nombre> --uri <url>',
92
+ 'cred.usage_sync': 'sync-cache --network <red>',
93
+ 'cred.usage_grant': 'grant-minter --network <red> --address <billetera>',
94
+ 'cred.usage_revoke_minter': 'revoke-minter --network <red> --address <billetera>',
95
+ 'cred.usage_revoke': 'revoke-credential --network <red> --token-id <id> --address <billetera> [--amount <n>]',
96
+ 'cred.usage_networks': 'Redes: celo, celoSepolia, base, baseSepolia',
97
+ 'cred.unknown_command': 'Orden desconocida: {{0}}',
98
+ 'cred.no_admin_key': 'Se requiere CREDENTIALS_PRIVATE_KEY para órdenes administrativas',
99
+ 'cred.key_missing': 'PRIVATE_KEY no configurada',
100
+ 'cred.key_or_admin_missing': 'CREDENTIALS_PRIVATE_KEY no configurada en .env',
101
+ 'cred.no_privkey': 'PRIVATE_KEY no configurada',
102
+ 'cred.emission_failed': 'Fallo en seguimiento de emisión: {{0}}',
103
+ 'cred.minted': 'Acuñado token {{0}} x{{1}} a {{2}} en {{3}}. TX: {{4}}',
104
+ 'cred.types_header': 'Tipos de credencial en {{0}} (nextTokenId: {{1}}):',
105
+ 'cred.type_row': ' {{0}}: {{1}} (soulbound: {{2}}, maxSupply: {{3}})',
106
+ 'cred.register_success': 'Tipo de credencial registrado. tokenId: {{0}} sitio: {{1}} tipo: {{2}} nombre: {{3}}',
107
+ 'cred.cache_failed': 'Fallo al actualizar caché: {{0}}',
108
+ 'cred.image_failed': 'Fallo en composición de imagen (tokenId {{0}}): {{1}}',
109
+ 'cred.recompose_title': 'Imagen recompuesta para token {{0}} ({{1}})',
110
+ 'cred.recompose_svg': ' SVG: {{0}}',
111
+ 'cred.recompose_png': ' PNG: {{0}}',
112
+ 'cred.token_not_in_cache': 'Token {{0}} no encontrado en caché credential_metadata. Regístrelo primero con register-type.',
113
+ 'cred.max_set': 'maxSupply establecido. tokenId: {{0}} max: {{1}} TX: {{2}}',
114
+ 'cred.uri_set': 'siteBaseURI establecido. sitio: {{0}} uri: {{1}} TX: {{2}}',
115
+ 'cred.granted': 'MINTER_ROLE otorgado a {{0}} en {{1}}. TX: {{2}}',
116
+ 'cred.revoked_minter': 'MINTER_ROLE revocado de {{0}} en {{1}}. TX: {{2}}',
117
+ 'cred.revoked': 'Token {{0}} x{{1}} revocado de {{2}} en {{3}}. TX: {{4}}',
118
+ 'cred.sync_row': ' Token {{0}} en caché: {{1}}',
119
+ 'cred.sync_done': 'Sincronización completa. {{0}} tokens en caché en {{1}}.',
120
+ 'cred.deploy_not_found': 'No se encontró despliegue para {{0}}. Despliegue primero o configure {{1}} en .env',
121
+ 'cred.unknown_network': 'Red desconocida: {{0}}',
122
+ 'cred.args_missing_mint': '--network, --token-id, --address requeridos',
123
+ 'cred.args_missing_register': '--network, --site, --type, --display requeridos',
124
+ 'cred.args_missing_recompose': '--token-id e --icon requeridos',
125
+ 'cred.args_missing_max': '--network, --token-id, --max requeridos',
126
+ 'cred.args_missing_uri': '--network, --site, --uri requeridos',
127
+ 'cred.args_missing_grant': '--network y --address requeridos',
128
+ 'cred.args_missing_revoke': '--network, --token-id, --address requeridos',
129
+ },
130
+ }, commonCli);
131
+ function getArg(args, name) {
132
+ const idx = args.indexOf(name);
133
+ return idx >= 0 && idx + 1 < args.length ? args[idx + 1] : null;
134
+ }
135
+ function hasFlag(args, name) {
136
+ return args.includes(name);
137
+ }
138
+ function getDeploymentsDir() {
139
+ const cwd = process.cwd();
140
+ const candidates = [
141
+ path.join(cwd, 'apps', 'hardhat', 'deployments', 'PasosDeJesusCredentials'),
142
+ path.join(cwd, '..', 'hardhat', 'deployments', 'PasosDeJesusCredentials'),
143
+ path.join(cwd, '..', '..', 'hardhat', 'deployments', 'PasosDeJesusCredentials'),
144
+ ];
145
+ for (const dir of candidates) {
146
+ if (fs.existsSync(dir))
147
+ return dir;
148
+ }
149
+ return candidates[0];
150
+ }
151
+ function getContractAddressFile(network) {
152
+ return path.join(getDeploymentsDir(), `${network}.json`);
153
+ }
154
+ function resolveAddress(network) {
155
+ const file = getContractAddressFile(network);
156
+ if (fs.existsSync(file)) {
157
+ return JSON.parse(fs.readFileSync(file, 'utf-8')).address;
158
+ }
159
+ const envKey = network.startsWith('base') ? 'NEXT_PUBLIC_PDJCREDENTIALS_BASE_ADDRESS' : 'NEXT_PUBLIC_PDJCREDENTIALS_CELO_ADDRESS';
160
+ const addr = process.env[envKey];
161
+ if (!addr)
162
+ throw new Error(ct('cred.deploy_not_found', network, envKey));
163
+ return addr;
164
+ }
165
+ async function getChain(network) {
166
+ const { celo, celoSepolia, base, baseSepolia } = await import('viem/chains');
167
+ switch (network) {
168
+ case 'celo': return celo;
169
+ case 'celoSepolia': return celoSepolia;
170
+ case 'base': return base;
171
+ case 'baseSepolia': return baseSepolia;
172
+ default: throw new Error(ct('cred.unknown_network', network));
173
+ }
174
+ }
175
+ async function getWalletClient(network, asAdmin) {
176
+ const { createWalletClient, http } = await import('viem');
177
+ const { privateKeyToAccount } = await import('viem/accounts');
178
+ const key = asAdmin
179
+ ? (process.env.CREDENTIALS_PRIVATE_KEY || process.env.PRIVATE_KEY)
180
+ : (process.env.PRIVATE_KEY || process.env.CREDENTIALS_PRIVATE_KEY);
181
+ if (!key)
182
+ throw new Error(ct('cred.key_or_admin_missing'));
183
+ const account = privateKeyToAccount(key);
184
+ return createWalletClient({ chain: await getChain(network), transport: http(), account });
185
+ }
186
+ async function getPublicClient(network) {
187
+ const { createPublicClient, http } = await import('viem');
188
+ return createPublicClient({ chain: await getChain(network), transport: http() });
189
+ }
190
+ async function newDb() {
191
+ const { Kysely, PostgresDialect } = await import('kysely');
192
+ const { Pool } = require('pg');
193
+ return new Kysely({
194
+ dialect: new PostgresDialect({
195
+ pool: new Pool({
196
+ host: process.env.PGHOST,
197
+ database: process.env.PGDATABASE,
198
+ user: process.env.PGUSER,
199
+ password: process.env.PGPASSWORD,
200
+ port: 5432,
201
+ }),
202
+ }),
203
+ });
204
+ }
205
+ function usage() {
206
+ console.log(ct('cred.usage_header'));
207
+ console.log('');
208
+ console.log(ct('cred.usage_commands'));
209
+ console.log(' ' + ct('cred.usage_list_types'));
210
+ console.log(' ' + ct('cred.usage_mint'));
211
+ console.log(' ' + ct('cred.usage_register'));
212
+ console.log(' ' + ct('cred.usage_register_opts'));
213
+ console.log(' ' + ct('cred.usage_register_icon'));
214
+ console.log(' ' + ct('cred.usage_recompose'));
215
+ console.log(' ' + ct('cred.usage_set_max'));
216
+ console.log(' ' + ct('cred.usage_set_uri'));
217
+ console.log(' ' + ct('cred.usage_sync'));
218
+ console.log(' ' + ct('cred.usage_grant'));
219
+ console.log(' ' + ct('cred.usage_revoke_minter'));
220
+ console.log(' ' + ct('cred.usage_revoke'));
221
+ console.log('');
222
+ console.log(ct('cred.usage_networks'));
223
+ }
224
+ async function listTypes(network) {
225
+ const pc = await getPublicClient(network);
226
+ const addr = resolveAddress(network);
227
+ const { default: credentialsAbi } = await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } });
228
+ const nextId = await pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'nextTokenId' });
229
+ console.log(ct('cred.types_header', network, String(nextId)));
230
+ for (let i = 1; i < Number(nextId); i++) {
231
+ try {
232
+ const name = await pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'tokenNames', args: [BigInt(i)] });
233
+ const sb = await pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'isSoulbound', args: [BigInt(i)] });
234
+ const max = await pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'maxSupply', args: [BigInt(i)] });
235
+ console.log(ct('cred.type_row', String(i), String(name), String(sb), String(max)));
236
+ }
237
+ catch { /* skip unconfigured */ }
238
+ }
239
+ }
240
+ async function mint(args) {
241
+ const net = getArg(args, '--network');
242
+ const tokenId = parseInt(getArg(args, '--token-id') || '');
243
+ const addr = getArg(args, '--address');
244
+ const amount = parseInt(getArg(args, '--amount') || '1');
245
+ if (!net || isNaN(tokenId) || !addr)
246
+ throw new Error(ct('cred.args_missing_mint'));
247
+ const key = process.env.PRIVATE_KEY;
248
+ if (!key)
249
+ throw new Error(ct('cred.no_privkey'));
250
+ const { createWalletClient, http } = await import('viem');
251
+ const { privateKeyToAccount } = await import('viem/accounts');
252
+ const { default: credentialsAbi } = await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } });
253
+ const account = privateKeyToAccount(key);
254
+ const wc = createWalletClient({ chain: await getChain(net), transport: http(), account });
255
+ const hash = await wc.writeContract({
256
+ address: resolveAddress(net),
257
+ abi: credentialsAbi,
258
+ functionName: 'mintCredential',
259
+ args: [addr, BigInt(tokenId), BigInt(amount)],
260
+ chain: wc.chain,
261
+ account: wc.account,
262
+ });
263
+ try {
264
+ const db = await newDb();
265
+ await db
266
+ .insertInto('credential_emission')
267
+ .values({ wallet_address: addr, token_id: tokenId, chain_id: net.includes('base') ? 'base' : 'celo' })
268
+ .onConflict((oc) => oc.columns(['wallet_address', 'token_id', 'chain_id']).doNothing())
269
+ .execute();
270
+ }
271
+ catch (e) {
272
+ console.warn(ct('cred.emission_failed', e.message));
273
+ }
274
+ console.log(ct('cred.minted', String(tokenId), String(amount), addr, net, hash));
275
+ }
276
+ async function registerType(args) {
277
+ const net = getArg(args, '--network');
278
+ const site = getArg(args, '--site');
279
+ const type = getArg(args, '--type');
280
+ const display = getArg(args, '--display');
281
+ const sb = getArg(args, '--soulbound') === 'true';
282
+ const courseId = parseInt(getArg(args, '--course-id') || '0');
283
+ const premium = hasFlag(args, '--premium');
284
+ const customURI = getArg(args, '--custom-uri') || '';
285
+ const maxSupply_ = parseInt(getArg(args, '--max-supply') || '0');
286
+ const icon = getArg(args, '--icon');
287
+ if (!net || !site || !type || !display)
288
+ throw new Error(ct('cred.args_missing_register'));
289
+ // registerCredentialType now uses MINTER_ROLE (backends can register)
290
+ const wc = await getWalletClient(net, false);
291
+ const pc = await getPublicClient(net);
292
+ const addr = resolveAddress(net);
293
+ const { default: credentialsAbi } = await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } });
294
+ const hash = await wc.writeContract({
295
+ address: addr,
296
+ abi: credentialsAbi,
297
+ functionName: 'registerCredentialType',
298
+ args: [site, type, display, sb, BigInt(courseId), premium, customURI, BigInt(maxSupply_)],
299
+ chain: wc.chain,
300
+ account: wc.account,
301
+ });
302
+ const receipt = await pc.waitForTransactionReceipt({ hash });
303
+ let tokenId = 0;
304
+ for (const log of receipt.logs) {
305
+ try {
306
+ const topics = log.topics;
307
+ if (topics && topics.length >= 2) {
308
+ tokenId = Number(BigInt(topics[1]));
309
+ break;
310
+ }
311
+ }
312
+ catch { }
313
+ }
314
+ if (tokenId === 0) {
315
+ const nextId = await pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'nextTokenId' });
316
+ tokenId = Number(nextId) - 1;
317
+ }
318
+ if (icon) {
319
+ try {
320
+ const { composeCredentialImage } = await import('../blockchain/credential-images.js');
321
+ await composeCredentialImage({
322
+ userSvgPath: icon,
323
+ tokenId,
324
+ site,
325
+ siteName: site,
326
+ type,
327
+ isSoulbound: sb,
328
+ isPremium: premium,
329
+ displayName: display,
330
+ publicDir: path.join(process.cwd(), 'public'),
331
+ pngCommand: 'rsvg-convert -w 512 -h 512 -o',
332
+ });
333
+ }
334
+ catch (err) {
335
+ console.warn(ct('cred.image_failed', String(tokenId), err.message));
336
+ }
337
+ }
338
+ try {
339
+ const db = await newDb();
340
+ await db
341
+ .insertInto('credential_metadata')
342
+ .values({
343
+ token_id: tokenId,
344
+ name: display,
345
+ type,
346
+ site,
347
+ is_premium: premium,
348
+ is_soulbound: sb,
349
+ image_url: `img/credential/${tokenId}.png`,
350
+ chain_id: net,
351
+ })
352
+ .onConflict((oc) => oc.columns(['token_id', 'chain_id']).doUpdateSet({
353
+ name: display, type, site,
354
+ is_premium: premium, is_soulbound: sb,
355
+ updated_at: new Date(),
356
+ }))
357
+ .execute();
358
+ }
359
+ catch (err) {
360
+ console.warn(ct('cred.cache_failed', err.message));
361
+ }
362
+ console.log(ct('cred.register_success', String(tokenId), site, type, display));
363
+ }
364
+ async function recomposeImage(args) {
365
+ const tokenId = parseInt(getArg(args, '--token-id') || '');
366
+ const net = getArg(args, '--network') || 'celo';
367
+ const icon = getArg(args, '--icon');
368
+ if (isNaN(tokenId) || !icon)
369
+ throw new Error(ct('cred.args_missing_recompose'));
370
+ const chainId = net;
371
+ const db = await newDb();
372
+ const row = await db
373
+ .selectFrom('credential_metadata')
374
+ .selectAll()
375
+ .where('token_id', '=', tokenId)
376
+ .where('chain_id', '=', chainId)
377
+ .executeTakeFirst();
378
+ if (!row)
379
+ throw new Error(ct('cred.token_not_in_cache', String(tokenId)));
380
+ const { composeCredentialImage } = await import('../blockchain/credential-images.js');
381
+ const { svgPath, pngPath } = await composeCredentialImage({
382
+ userSvgPath: icon,
383
+ tokenId,
384
+ site: row.site,
385
+ siteName: row.site,
386
+ type: row.type,
387
+ isSoulbound: row.is_soulbound ?? true,
388
+ isPremium: row.is_premium,
389
+ displayName: row.name,
390
+ publicDir: path.join(process.cwd(), 'public'),
391
+ pngCommand: 'rsvg-convert -w 512 -h 512 -o',
392
+ });
393
+ console.log(ct('cred.recompose_title', String(tokenId), String(row.name)));
394
+ console.log(ct('cred.recompose_svg', svgPath));
395
+ console.log(ct('cred.recompose_png', pngPath));
396
+ }
397
+ async function setMaxSupply(args) {
398
+ const net = getArg(args, '--network');
399
+ const tokenId = parseInt(getArg(args, '--token-id') || '');
400
+ const max = parseInt(getArg(args, '--max') || '');
401
+ if (!net || isNaN(tokenId) || isNaN(max))
402
+ throw new Error(ct('cred.args_missing_max'));
403
+ const wc = await getWalletClient(net, true);
404
+ const hash = await wc.writeContract({
405
+ address: resolveAddress(net),
406
+ abi: (await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } })).default,
407
+ functionName: 'setMaxSupply',
408
+ args: [BigInt(tokenId), BigInt(max)],
409
+ chain: wc.chain,
410
+ account: wc.account,
411
+ });
412
+ console.log(ct('cred.max_set', String(tokenId), String(max), hash));
413
+ }
414
+ async function setSiteUri(args) {
415
+ const net = getArg(args, '--network');
416
+ const site = getArg(args, '--site');
417
+ const uri = getArg(args, '--uri');
418
+ if (!net || !site || !uri)
419
+ throw new Error(ct('cred.args_missing_uri'));
420
+ const wc = await getWalletClient(net, true);
421
+ const hash = await wc.writeContract({
422
+ address: resolveAddress(net),
423
+ abi: (await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } })).default,
424
+ functionName: 'setSiteBaseURI',
425
+ args: [site, uri],
426
+ chain: wc.chain,
427
+ account: wc.account,
428
+ });
429
+ console.log(ct('cred.uri_set', site, uri, hash));
430
+ }
431
+ async function syncCache(network) {
432
+ const pc = await getPublicClient(network);
433
+ const addr = resolveAddress(network);
434
+ const db = await newDb();
435
+ const { default: credentialsAbi } = await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } });
436
+ const siteMap = {
437
+ '0x616d78ebe5052eab0de25afa2c90b2cd8e550c70dfa0859ede7a9902335187ba': 'learn.tg',
438
+ '0x2eb33b8b6fa0a4cfa9282cd213f79e291a0a473487dd04b15bc4c9f11bffc6d9': 'sivel.xyz',
439
+ '0xc881844ff5e225c937d57807a73752d627e798112994cb398c38727eb732e580': 'stable-sl.pdJ.app',
440
+ };
441
+ const typeMap = {
442
+ '0x65a58990984f61f252fd6868ac0e9acb3befaf5d04a573bd155fc6c2e4159d9c': 'course_completion',
443
+ '0xa0a8be0a778a94eac2488e69eb5cf6921d2c02275d181a1189a6745aa6626f87': 'role',
444
+ '0x7a4833216f98c32023a615acdd8ead93c40086e7f9ba61c1d0d47dc1e8f0f174': 'achievement',
445
+ '0x7dd481eb4b63b94bb55e6b98aabb06c3b8484f82a4d656d6bca0b0cf9b446be0': 'nft',
446
+ };
447
+ let count = 0;
448
+ const nextId = await pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'nextTokenId' });
449
+ for (let i = 1; i < Number(nextId); i++) {
450
+ try {
451
+ const [name, sb, premium, siteHash, typeHash] = await Promise.all([
452
+ pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'tokenNames', args: [BigInt(i)] }),
453
+ pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'isSoulbound', args: [BigInt(i)] }),
454
+ pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'isPremiumCourse', args: [BigInt(i)] }),
455
+ pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'tokenSiteHash', args: [BigInt(i)] }),
456
+ pc.readContract({ address: addr, abi: credentialsAbi, functionName: 'tokenTypeHash', args: [BigInt(i)] }),
457
+ ]);
458
+ if (!name || name === '')
459
+ continue;
460
+ const site = siteMap[siteHash] || 'unknown';
461
+ const type = typeMap[typeHash] || 'unknown';
462
+ await db
463
+ .insertInto('credential_metadata')
464
+ .values({
465
+ token_id: i, name: name, type, site,
466
+ is_premium: premium, is_soulbound: sb,
467
+ image_url: `img/credential/${i}.png`,
468
+ chain_id: network,
469
+ })
470
+ .onConflict((oc) => oc.columns(['token_id', 'chain_id']).doUpdateSet({
471
+ name: name, type, site,
472
+ is_soulbound: sb, is_premium: premium,
473
+ updated_at: new Date(),
474
+ }))
475
+ .execute();
476
+ count++;
477
+ console.log(ct('cred.sync_row', String(i), String(name)));
478
+ }
479
+ catch { /* skip */ }
480
+ }
481
+ console.log(ct('cred.sync_done', String(count), network));
482
+ }
483
+ async function grantMinter(args) {
484
+ const net = getArg(args, '--network');
485
+ const addr = getArg(args, '--address');
486
+ if (!net || !addr)
487
+ throw new Error(ct('cred.args_missing_grant'));
488
+ const wc = await getWalletClient(net, true);
489
+ const hash = await wc.writeContract({
490
+ address: resolveAddress(net),
491
+ abi: (await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } })).default,
492
+ functionName: 'grantRole',
493
+ args: ['0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6', addr],
494
+ chain: wc.chain,
495
+ account: wc.account,
496
+ });
497
+ console.log(ct('cred.granted', addr, net, hash));
498
+ }
499
+ async function revokeMinter(args) {
500
+ const net = getArg(args, '--network');
501
+ const addr = getArg(args, '--address');
502
+ if (!net || !addr)
503
+ throw new Error(ct('cred.args_missing_grant'));
504
+ const wc = await getWalletClient(net, true);
505
+ const hash = await wc.writeContract({
506
+ address: resolveAddress(net),
507
+ abi: (await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } })).default,
508
+ functionName: 'revokeRole',
509
+ args: ['0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6', addr],
510
+ chain: wc.chain,
511
+ account: wc.account,
512
+ });
513
+ console.log(ct('cred.revoked_minter', addr, net, hash));
514
+ }
515
+ async function revokeCredential(args) {
516
+ const net = getArg(args, '--network');
517
+ const tokenId = parseInt(getArg(args, '--token-id') || '');
518
+ const addr = getArg(args, '--address');
519
+ const amount = parseInt(getArg(args, '--amount') || '1');
520
+ if (!net || isNaN(tokenId) || !addr)
521
+ throw new Error(ct('cred.args_missing_revoke'));
522
+ const key = process.env.PRIVATE_KEY;
523
+ if (!key)
524
+ throw new Error(ct('cred.no_privkey'));
525
+ const { createWalletClient, http } = await import('viem');
526
+ const { privateKeyToAccount } = await import('viem/accounts');
527
+ const { default: credentialsAbi } = await import('../abis/PasosDeJesusCredentials.json', { with: { type: 'json' } });
528
+ const account = privateKeyToAccount(key);
529
+ const wc = createWalletClient({ chain: await getChain(net), transport: http(), account });
530
+ const hash = await wc.writeContract({
531
+ address: resolveAddress(net),
532
+ abi: credentialsAbi,
533
+ functionName: 'revokeCredential',
534
+ args: [addr, BigInt(tokenId), BigInt(amount)],
535
+ chain: wc.chain,
536
+ account: wc.account,
537
+ });
538
+ console.log(ct('cred.revoked', String(tokenId), String(amount), addr, net, hash));
539
+ }
540
+ function isAdminCommand(cmd) {
541
+ return cmd === 'grant-minter' || cmd === 'revoke-minter' || cmd === 'set-site-uri';
542
+ }
543
+ function deploymentsExist() {
544
+ return fs.existsSync(getDeploymentsDir());
545
+ }
546
+ function hasAdminKey() {
547
+ return !!process.env.CREDENTIALS_PRIVATE_KEY;
548
+ }
549
+ export async function runCredentials(args) {
550
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
551
+ usage();
552
+ process.exit(0);
553
+ }
554
+ const command = args[0];
555
+ const rest = args.slice(1);
556
+ if (isAdminCommand(command) && !hasAdminKey()) {
557
+ problem(ct('cred.no_admin_key'));
558
+ process.exit(1);
559
+ }
560
+ try {
561
+ switch (command) {
562
+ case 'list-types': {
563
+ const net = getArg(rest, '--network') || process.env.NEXT_PUBLIC_NETWORK || 'celoSepolia';
564
+ await listTypes(net);
565
+ break;
566
+ }
567
+ case 'mint': {
568
+ await mint(rest);
569
+ break;
570
+ }
571
+ case 'register-type': {
572
+ await registerType(rest);
573
+ break;
574
+ }
575
+ case 'recompose-image': {
576
+ await recomposeImage(rest);
577
+ break;
578
+ }
579
+ case 'set-max-supply': {
580
+ await setMaxSupply(rest);
581
+ break;
582
+ }
583
+ case 'set-site-uri': {
584
+ if (!hasAdminKey()) {
585
+ problem(ct('cred.no_admin_key'));
586
+ process.exit(1);
587
+ }
588
+ await setSiteUri(rest);
589
+ break;
590
+ }
591
+ case 'sync-cache': {
592
+ const net = getArg(rest, '--network') || process.env.NEXT_PUBLIC_NETWORK || 'celoSepolia';
593
+ await syncCache(net);
594
+ break;
595
+ }
596
+ case 'grant-minter': {
597
+ if (!hasAdminKey()) {
598
+ problem(ct('cred.no_admin_key'));
599
+ process.exit(1);
600
+ }
601
+ await grantMinter(rest);
602
+ break;
603
+ }
604
+ case 'revoke-minter': {
605
+ if (!hasAdminKey()) {
606
+ problem(ct('cred.no_admin_key'));
607
+ process.exit(1);
608
+ }
609
+ await revokeMinter(rest);
610
+ break;
611
+ }
612
+ case 'revoke-credential': {
613
+ await revokeCredential(rest);
614
+ break;
615
+ }
616
+ default:
617
+ problem(ct('cred.unknown_command', command));
618
+ usage();
619
+ process.exit(1);
620
+ }
621
+ }
622
+ catch (err) {
623
+ problem(err.message || String(err));
624
+ process.exit(1);
625
+ }
626
+ }
627
+ export { deploymentsExist, hasAdminKey };
@@ -0,0 +1 @@
1
+ export declare function runAiPrinciples(): Promise<void>;
@@ -0,0 +1,37 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { findIaDir } from '@pasosdejesus/m/dist/tasks/util.js';
4
+ import { success, problem } from '@pasosdejesus/m/dist/util/colors.js';
5
+ import { setClipboard } from '@pasosdejesus/m/dist/tasks/edit.js';
6
+ import { createTranslator } from '@pasosdejesus/m/dist/i18n/createTranslator.js';
7
+ import { detectLocale } from '@pasosdejesus/m/dist/util/locale.js';
8
+ import commonCli from '@pasosdejesus/m/dist/i18n/common-cli.js';
9
+ const t = createTranslator(detectLocale(), {
10
+ en: {
11
+ 'principles.no_ia_dir': 'ia/ directory not found in the project.',
12
+ 'principles.read_error': 'Error reading ia/principles.md',
13
+ 'principles.copied': '📋 principles.md copied to clipboard ({{0}}KB)',
14
+ },
15
+ es: {
16
+ 'principles.no_ia_dir': 'No se encontró ia/ en el proyecto.',
17
+ 'principles.read_error': 'Error al leer ia/principles.md',
18
+ 'principles.copied': '📋 principles.md copiado al portapapeles ({{0}}KB)',
19
+ },
20
+ }, commonCli);
21
+ export async function runAiPrinciples() {
22
+ const iaDir = await findIaDir();
23
+ if (!iaDir) {
24
+ problem(t('principles.no_ia_dir'));
25
+ return;
26
+ }
27
+ const principlesPath = join(iaDir, 'principles.md');
28
+ try {
29
+ const content = await readFile(principlesPath, 'utf-8');
30
+ console.log(content);
31
+ await setClipboard(content);
32
+ success(t('principles.copied', Math.round(content.length / 1024).toString()));
33
+ }
34
+ catch {
35
+ problem(t('principles.read_error'));
36
+ }
37
+ }