@agirails/sdk 2.3.3 → 2.4.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.
Files changed (67) hide show
  1. package/README.md +10 -12
  2. package/dist/ACTPClient.d.ts +80 -3
  3. package/dist/ACTPClient.d.ts.map +1 -1
  4. package/dist/ACTPClient.js +213 -57
  5. package/dist/ACTPClient.js.map +1 -1
  6. package/dist/adapters/BasicAdapter.d.ts +13 -1
  7. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  8. package/dist/adapters/BasicAdapter.js +24 -3
  9. package/dist/adapters/BasicAdapter.js.map +1 -1
  10. package/dist/cli/commands/init.d.ts.map +1 -1
  11. package/dist/cli/commands/init.js +9 -292
  12. package/dist/cli/commands/init.js.map +1 -1
  13. package/dist/cli/commands/publish.d.ts +11 -3
  14. package/dist/cli/commands/publish.d.ts.map +1 -1
  15. package/dist/cli/commands/publish.js +319 -80
  16. package/dist/cli/commands/publish.js.map +1 -1
  17. package/dist/cli/commands/register.d.ts.map +1 -1
  18. package/dist/cli/commands/register.js +10 -0
  19. package/dist/cli/commands/register.js.map +1 -1
  20. package/dist/cli/utils/config.d.ts +3 -2
  21. package/dist/cli/utils/config.d.ts.map +1 -1
  22. package/dist/cli/utils/config.js +9 -1
  23. package/dist/cli/utils/config.js.map +1 -1
  24. package/dist/cli/utils/wallet.d.ts +31 -0
  25. package/dist/cli/utils/wallet.d.ts.map +1 -0
  26. package/dist/cli/utils/wallet.js +114 -0
  27. package/dist/cli/utils/wallet.js.map +1 -0
  28. package/dist/config/pendingPublish.d.ts +79 -0
  29. package/dist/config/pendingPublish.d.ts.map +1 -0
  30. package/dist/config/pendingPublish.js +177 -0
  31. package/dist/config/pendingPublish.js.map +1 -0
  32. package/dist/config/publishPipeline.d.ts +33 -0
  33. package/dist/config/publishPipeline.d.ts.map +1 -1
  34. package/dist/config/publishPipeline.js +33 -2
  35. package/dist/config/publishPipeline.js.map +1 -1
  36. package/dist/index.d.ts +2 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +7 -3
  39. package/dist/index.js.map +1 -1
  40. package/dist/wallet/AutoWalletProvider.d.ts +2 -1
  41. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  42. package/dist/wallet/AutoWalletProvider.js +6 -2
  43. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  44. package/dist/wallet/IWalletProvider.d.ts +4 -2
  45. package/dist/wallet/IWalletProvider.d.ts.map +1 -1
  46. package/dist/wallet/aa/TransactionBatcher.d.ts +54 -0
  47. package/dist/wallet/aa/TransactionBatcher.d.ts.map +1 -1
  48. package/dist/wallet/aa/TransactionBatcher.js +67 -1
  49. package/dist/wallet/aa/TransactionBatcher.js.map +1 -1
  50. package/dist/wallet/keystore.d.ts.map +1 -1
  51. package/dist/wallet/keystore.js +17 -7
  52. package/dist/wallet/keystore.js.map +1 -1
  53. package/package.json +2 -2
  54. package/src/ACTPClient.ts +265 -49
  55. package/src/adapters/BasicAdapter.ts +48 -12
  56. package/src/cli/commands/init.ts +7 -348
  57. package/src/cli/commands/publish.ts +354 -87
  58. package/src/cli/commands/register.ts +14 -0
  59. package/src/cli/utils/config.ts +11 -2
  60. package/src/cli/utils/wallet.ts +109 -0
  61. package/src/config/pendingPublish.ts +237 -0
  62. package/src/config/publishPipeline.ts +82 -1
  63. package/src/index.ts +8 -0
  64. package/src/wallet/AutoWalletProvider.ts +7 -2
  65. package/src/wallet/IWalletProvider.ts +4 -2
  66. package/src/wallet/aa/TransactionBatcher.ts +113 -0
  67. package/src/wallet/keystore.ts +25 -9
@@ -10,7 +10,6 @@
10
10
  import * as crypto from 'crypto';
11
11
  import * as fs from 'fs';
12
12
  import * as path from 'path';
13
- import * as readline from 'readline';
14
13
  import { Command } from 'commander';
15
14
  import {
16
15
  saveConfig,
@@ -21,6 +20,7 @@ import {
21
20
  CLIMode,
22
21
  } from '../utils/config';
23
22
  import { Output, ExitCode } from '../utils/output';
23
+ import { generateWallet, computeSmartWalletInit } from '../utils/wallet';
24
24
  import { MockStateManager } from '../../runtime/MockStateManager';
25
25
 
26
26
  // ============================================================================
@@ -167,7 +167,6 @@ async function runInit(options: InitOptions, output: Output, cmd?: Command): Pro
167
167
  // Get or generate address
168
168
  let address = options.address;
169
169
  let smartWalletAddress: string | undefined;
170
- let didRegister = false;
171
170
  if (!address) {
172
171
  if (mode === 'mock') {
173
172
  // Generate a random address for mock mode
@@ -182,16 +181,11 @@ async function runInit(options: InitOptions, output: Output, cmd?: Command): Pro
182
181
  if (walletType === 'auto') {
183
182
  // Compute Smart Wallet address from signer
184
183
  smartWalletAddress = await computeSmartWalletInit(eoaAddress, mode, output);
184
+ address = smartWalletAddress;
185
185
 
186
- // Y/N: Register for gas-free transactions?
187
- const shouldRegister = await promptRegister(output);
188
- if (shouldRegister) {
189
- didRegister = await runInlineRegistration(projectRoot, mode, output);
190
- }
191
-
192
- // address = Smart Wallet if registered, EOA if not
193
- // This ensures CLI commands (balance, tx list) show the correct address
194
- address = didRegister ? smartWalletAddress : eoaAddress;
186
+ output.info('');
187
+ output.info('Gas-free transactions enabled (activates on first payment)');
188
+ output.info('Next: run "actp publish" to publish your agent config');
195
189
  } else {
196
190
  address = eoaAddress;
197
191
  }
@@ -213,7 +207,6 @@ async function runInit(options: InitOptions, output: Output, cmd?: Command): Pro
213
207
  version: '1.0',
214
208
  ...(walletType !== 'mock' && { wallet: walletType as 'auto' | 'eoa' }),
215
209
  ...(smartWalletAddress && { smartWallet: smartWalletAddress.toLowerCase() }),
216
- ...(didRegister && { registered: true }),
217
210
  // AGIRAILS.md-derived values (stored for downstream use)
218
211
  ...(mdConfig && mdConfig.name ? { agentName: String(mdConfig.name) } : {}),
219
212
  ...(mdConfig && mdConfig.intent ? { intent: String(mdConfig.intent) as 'earn' | 'pay' | 'both' } : {}),
@@ -270,14 +263,8 @@ async function runInit(options: InitOptions, output: Output, cmd?: Command): Pro
270
263
  } else {
271
264
  output.blank();
272
265
  output.print('Next steps:');
273
- if (walletType === 'auto' && didRegister) {
274
- // Already registered ready to go
275
- output.print(' 1. Create a payment: actp pay <provider> <amount>');
276
- output.print(' 2. Check your balance: actp balance');
277
- output.print(' 3. List transactions: actp tx list');
278
- } else if (walletType === 'auto') {
279
- // Skipped registration — remind them
280
- output.print(' 1. Register for gas-free: actp register');
266
+ if (walletType === 'auto') {
267
+ output.print(' 1. Publish config: actp publish');
281
268
  output.print(' 2. Create a payment: actp pay <provider> <amount>');
282
269
  output.print(' 3. Check your balance: actp balance');
283
270
  } else {
@@ -290,334 +277,6 @@ async function runInit(options: InitOptions, output: Output, cmd?: Command): Pro
290
277
  }
291
278
  }
292
279
 
293
- // ============================================================================
294
- // Wallet Generation
295
- // ============================================================================
296
-
297
- async function generateWallet(actpDir: string, output: Output): Promise<string> {
298
- const { Wallet } = await import('ethers');
299
-
300
- const wallet = Wallet.createRandom();
301
-
302
- // Get password from env var or interactive prompt
303
- let password = process.env.ACTP_KEY_PASSWORD;
304
- if (!password) {
305
- password = await promptPassword();
306
- }
307
-
308
- if (!password || password.length < 8) {
309
- throw new Error(
310
- 'Wallet password required (minimum 8 characters).\n' +
311
- 'Set ACTP_KEY_PASSWORD env var or enter when prompted.'
312
- );
313
- }
314
-
315
- // Encrypt with Keystore V3 (scrypt + AES-128-CTR)
316
- output.info('Encrypting wallet (this takes a few seconds)...');
317
- const keystore = await wallet.encrypt(password);
318
-
319
- // Save with restrictive permissions
320
- const keystorePath = path.join(actpDir, 'keystore.json');
321
- fs.writeFileSync(keystorePath, keystore, { mode: 0o600 });
322
-
323
- output.success('Key securely saved and encrypted');
324
- output.info(`Address: ${wallet.address}`);
325
- output.warning('Back up your password — it cannot be recovered.');
326
- output.info('');
327
- output.info('To start your agent:');
328
- output.info(' export ACTP_KEY_PASSWORD="your-password"');
329
- output.info(' npx ts-node agent.ts');
330
-
331
- return wallet.address;
332
- }
333
-
334
- /**
335
- * Compute the Smart Wallet address for an EOA signer.
336
- * Uses CREATE2 counterfactual derivation — no deployment needed.
337
- */
338
- async function computeSmartWalletInit(
339
- eoaAddress: string,
340
- mode: string,
341
- output: Output
342
- ): Promise<string> {
343
- const { ethers } = await import('ethers');
344
- const { getNetwork } = await import('../../config/networks');
345
- const { computeSmartWalletAddress } = await import('../../wallet/aa/UserOpBuilder');
346
-
347
- const network = mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
348
- const networkConfig = getNetwork(network);
349
- const rpcUrl = networkConfig.rpcUrl;
350
- const provider = new ethers.JsonRpcProvider(rpcUrl);
351
-
352
- output.info('Computing Smart Wallet address...');
353
- const smartWalletAddress = await computeSmartWalletAddress(eoaAddress, provider);
354
-
355
- output.success(`Smart Wallet: ${smartWalletAddress}`);
356
- output.info('Gas-free transactions enabled (requires registration)');
357
- output.info('Register with: actp register');
358
-
359
- return smartWalletAddress;
360
- }
361
-
362
- /**
363
- * Ask user if they want to register for gas-free transactions.
364
- * Non-TTY (piped/agent) defaults to yes.
365
- */
366
- async function promptRegister(output: Output): Promise<boolean> {
367
- output.blank();
368
- output.print('Register for gas-free transactions? (recommended)');
369
- output.print(' Your agent gets a Smart Wallet with sponsored gas — no ETH needed.');
370
- output.print(' Requires on-chain registration on AgentRegistry.');
371
- output.blank();
372
-
373
- if (!process.stdin.isTTY) {
374
- output.info('Non-interactive mode: auto-registering');
375
- return true;
376
- }
377
-
378
- const rl = readline.createInterface({
379
- input: process.stdin,
380
- output: process.stdout,
381
- });
382
-
383
- return new Promise((resolve) => {
384
- rl.question(' Register now? [Y/n] ', (answer) => {
385
- rl.close();
386
- const trimmed = answer.trim().toLowerCase();
387
- resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');
388
- });
389
- });
390
- }
391
-
392
- /**
393
- * Run inline registration during init.
394
- * Reuses the same logic as `actp register` — parses AGIRAILS.md,
395
- * builds gasless UserOp (testnet: register + mint 1000 USDC).
396
- *
397
- * Returns true if registration succeeded, false on failure (non-fatal).
398
- */
399
- async function runInlineRegistration(
400
- projectRoot: string,
401
- mode: string,
402
- output: Output
403
- ): Promise<boolean> {
404
- try {
405
- const { resolvePrivateKey } = await import('../../wallet/keystore');
406
- const privateKey = await resolvePrivateKey(projectRoot);
407
- if (!privateKey) {
408
- output.warning('Could not load wallet key. Run "actp register" later.');
409
- return false;
410
- }
411
-
412
- const { parseAgirailsMd } = await import('../../config/agirailsmd');
413
- const { extractRegistrationParams } = await import('../../config/publishPipeline');
414
- const { ethers } = await import('ethers');
415
- const { getNetwork } = await import('../../config/networks');
416
- const { AutoWalletProvider } = await import('../../wallet/AutoWalletProvider');
417
- const { buildRegisterAgentBatch, buildTestnetInitBatch, buildTestnetMintBatch } = await import('../../wallet/aa/TransactionBatcher');
418
- const { sdkLogger } = await import('../../utils/Logger');
419
-
420
- // Parse AGIRAILS.md if present
421
- const agirailsMdPath = path.join(projectRoot, 'AGIRAILS.md');
422
- let endpoint = '';
423
- let serviceDescriptors;
424
-
425
- if (fs.existsSync(agirailsMdPath)) {
426
- const content = fs.readFileSync(agirailsMdPath, 'utf-8');
427
- const parsed = parseAgirailsMd(content);
428
- const regParams = extractRegistrationParams(parsed.frontmatter);
429
- endpoint = regParams.endpoint;
430
- serviceDescriptors = regParams.serviceDescriptors;
431
- output.info(`Parsed ${serviceDescriptors.length} service(s) from AGIRAILS.md`);
432
- } else {
433
- const serviceType = 'general';
434
- endpoint = 'https://agirails.io/agent/pending';
435
- serviceDescriptors = [{
436
- serviceTypeHash: ethers.keccak256(ethers.toUtf8Bytes(serviceType)),
437
- serviceType,
438
- schemaURI: '',
439
- minPrice: 0n,
440
- maxPrice: 1_000_000_000n,
441
- avgCompletionTime: 3600,
442
- metadataCID: '',
443
- }];
444
- output.info('No AGIRAILS.md found. Using default "general" service.');
445
- }
446
-
447
- const network = mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
448
- const networkConfig = getNetwork(network);
449
-
450
- if (!networkConfig.aa || !networkConfig.contracts.agentRegistry) {
451
- output.warning('AA or AgentRegistry not configured. Run "actp register" later.');
452
- return false;
453
- }
454
-
455
- // Check for valid bundler/paymaster URL (CDP_API_KEY must be set)
456
- const cdpUrl = networkConfig.aa.bundlerUrls.coinbase;
457
- const hasPimlico = !!networkConfig.aa.bundlerUrls.pimlico;
458
- if (cdpUrl.endsWith('/') && !hasPimlico) {
459
- output.warning('CDP_API_KEY not set. Skipping registration.');
460
- output.info('Set CDP_API_KEY and run "actp register" later.');
461
- return false;
462
- }
463
-
464
- const provider = new ethers.JsonRpcProvider(networkConfig.rpcUrl);
465
- const signer = new ethers.Wallet(privateKey, provider);
466
-
467
- const autoWallet = await AutoWalletProvider.create({
468
- signer,
469
- provider,
470
- chainId: networkConfig.chainId,
471
- actpKernelAddress: networkConfig.contracts.actpKernel,
472
- bundler: {
473
- primaryUrl: networkConfig.aa.bundlerUrls.coinbase,
474
- backupUrl: networkConfig.aa.bundlerUrls.pimlico,
475
- },
476
- paymaster: {
477
- primaryUrl: networkConfig.aa.paymasterUrls.coinbase,
478
- backupUrl: networkConfig.aa.paymasterUrls.pimlico,
479
- },
480
- });
481
-
482
- const smartWalletAddress = autoWallet.getAddress();
483
-
484
- // Build and submit registration batch
485
- if (mode === 'testnet') {
486
- // Testnet: try combined batch first, fall back to separate UserOps
487
- // Some paymaster configurations reject multi-target batches in simulation
488
- output.info('Testnet: registering + minting 1000 test USDC...');
489
-
490
- const combinedCalls = buildTestnetInitBatch({
491
- agentRegistryAddress: networkConfig.contracts.agentRegistry,
492
- endpoint,
493
- serviceDescriptors,
494
- mockUsdcAddress: networkConfig.contracts.usdc,
495
- recipient: smartWalletAddress,
496
- mintAmount: '1000000000',
497
- });
498
-
499
- let receipt;
500
- try {
501
- const txRequests = combinedCalls.map((c) => ({
502
- to: c.target,
503
- data: c.data,
504
- value: c.value.toString(),
505
- }));
506
- receipt = await autoWallet.sendBatchTransaction(txRequests);
507
- } catch (combinedError) {
508
- // Combined batch failed — split into two separate UserOps
509
- output.info('Combined batch failed, trying separate transactions...');
510
- sdkLogger.warn('Combined testnet batch failed, splitting', {
511
- error: combinedError instanceof Error ? combinedError.message : String(combinedError),
512
- });
513
-
514
- // Step 1: Register agent only
515
- const registerCalls = buildRegisterAgentBatch(
516
- networkConfig.contracts.agentRegistry,
517
- endpoint,
518
- serviceDescriptors
519
- );
520
- const registerTxs = registerCalls.map((c) => ({
521
- to: c.target,
522
- data: c.data,
523
- value: c.value.toString(),
524
- }));
525
-
526
- const registerReceipt = await autoWallet.sendBatchTransaction(registerTxs);
527
- if (!registerReceipt.success) {
528
- output.warning(`Registration failed (tx: ${registerReceipt.hash}). Run "actp register" later.`);
529
- return false;
530
- }
531
- output.success('Agent registered on AgentRegistry');
532
- output.print(` Tx: ${registerReceipt.hash}`);
533
-
534
- // Step 2: Mint test USDC
535
- try {
536
- const mintCalls = buildTestnetMintBatch(
537
- networkConfig.contracts.usdc,
538
- smartWalletAddress,
539
- '1000000000'
540
- );
541
- const mintTxs = mintCalls.map((c) => ({
542
- to: c.target,
543
- data: c.data,
544
- value: c.value.toString(),
545
- }));
546
- const mintReceipt = await autoWallet.sendBatchTransaction(mintTxs);
547
- if (mintReceipt.success) {
548
- output.success('Minted 1,000 test USDC to Smart Wallet');
549
- output.print(` Tx: ${mintReceipt.hash}`);
550
- } else {
551
- output.warning('USDC mint failed. Mint manually: actp faucet');
552
- }
553
- } catch (mintError) {
554
- output.warning('USDC mint failed. Mint manually: actp faucet');
555
- sdkLogger.warn('Testnet mint failed', {
556
- error: mintError instanceof Error ? mintError.message : String(mintError),
557
- });
558
- }
559
- return true;
560
- }
561
-
562
- if (!receipt.success) {
563
- output.warning(`Registration failed (tx: ${receipt.hash}). Run "actp register" later.`);
564
- return false;
565
- }
566
-
567
- output.success('Agent registered on AgentRegistry');
568
- output.success('Minted 1,000 test USDC to Smart Wallet');
569
- output.print(` Tx: ${receipt.hash}`);
570
- } else {
571
- // Mainnet: register only (no minting)
572
- output.info('Registering on AgentRegistry (gasless)...');
573
- const calls = buildRegisterAgentBatch(
574
- networkConfig.contracts.agentRegistry,
575
- endpoint,
576
- serviceDescriptors
577
- );
578
- const txRequests = calls.map((c) => ({
579
- to: c.target,
580
- data: c.data,
581
- value: c.value.toString(),
582
- }));
583
-
584
- const receipt = await autoWallet.sendBatchTransaction(txRequests);
585
- if (!receipt.success) {
586
- output.warning(`Registration failed (tx: ${receipt.hash}). Run "actp register" later.`);
587
- return false;
588
- }
589
-
590
- output.success('Agent registered on AgentRegistry');
591
- output.print(` Tx: ${receipt.hash}`);
592
- }
593
-
594
- return true;
595
- } catch (error) {
596
- output.warning(`Registration failed: ${(error as Error).message}`);
597
- output.info('You can register later with: actp register');
598
- return false;
599
- }
600
- }
601
-
602
- async function promptPassword(): Promise<string> {
603
- // If not a TTY (e.g. piped or run by agent), skip prompt
604
- if (!process.stdin.isTTY) {
605
- return '';
606
- }
607
-
608
- const rl = readline.createInterface({
609
- input: process.stdin,
610
- output: process.stdout,
611
- });
612
-
613
- return new Promise((resolve) => {
614
- rl.question('Enter password for wallet encryption (min 8 chars): ', (answer) => {
615
- rl.close();
616
- resolve(answer.trim());
617
- });
618
- });
619
- }
620
-
621
280
  // ============================================================================
622
281
  // Scaffold
623
282
  // ============================================================================