@agirails/sdk 2.2.3 → 2.3.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 (211) hide show
  1. package/README.md +65 -31
  2. package/dist/ACTPClient.d.ts +42 -1
  3. package/dist/ACTPClient.d.ts.map +1 -1
  4. package/dist/ACTPClient.js +207 -22
  5. package/dist/ACTPClient.js.map +1 -1
  6. package/dist/abi/AgentRegistry.json +133 -0
  7. package/dist/adapters/AdapterRouter.d.ts.map +1 -1
  8. package/dist/adapters/AdapterRouter.js.map +1 -1
  9. package/dist/adapters/BasicAdapter.d.ts +10 -1
  10. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  11. package/dist/adapters/BasicAdapter.js +36 -1
  12. package/dist/adapters/BasicAdapter.js.map +1 -1
  13. package/dist/adapters/X402Adapter.d.ts +34 -7
  14. package/dist/adapters/X402Adapter.d.ts.map +1 -1
  15. package/dist/adapters/X402Adapter.js +36 -8
  16. package/dist/adapters/X402Adapter.js.map +1 -1
  17. package/dist/adapters/index.d.ts +1 -1
  18. package/dist/adapters/index.d.ts.map +1 -1
  19. package/dist/adapters/index.js.map +1 -1
  20. package/dist/cli/commands/diff.d.ts +11 -0
  21. package/dist/cli/commands/diff.d.ts.map +1 -0
  22. package/dist/cli/commands/diff.js +115 -0
  23. package/dist/cli/commands/diff.js.map +1 -0
  24. package/dist/cli/commands/init.d.ts +1 -0
  25. package/dist/cli/commands/init.d.ts.map +1 -1
  26. package/dist/cli/commands/init.js +260 -19
  27. package/dist/cli/commands/init.js.map +1 -1
  28. package/dist/cli/commands/publish.d.ts +11 -0
  29. package/dist/cli/commands/publish.d.ts.map +1 -0
  30. package/dist/cli/commands/publish.js +170 -0
  31. package/dist/cli/commands/publish.js.map +1 -0
  32. package/dist/cli/commands/pull.d.ts +12 -0
  33. package/dist/cli/commands/pull.d.ts.map +1 -0
  34. package/dist/cli/commands/pull.js +99 -0
  35. package/dist/cli/commands/pull.js.map +1 -0
  36. package/dist/cli/commands/register.d.ts +16 -0
  37. package/dist/cli/commands/register.d.ts.map +1 -0
  38. package/dist/cli/commands/register.js +211 -0
  39. package/dist/cli/commands/register.js.map +1 -0
  40. package/dist/cli/index.js +10 -0
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/cli/utils/config.d.ts +6 -0
  43. package/dist/cli/utils/config.d.ts.map +1 -1
  44. package/dist/cli/utils/config.js.map +1 -1
  45. package/dist/config/agirailsmd.d.ts +94 -0
  46. package/dist/config/agirailsmd.d.ts.map +1 -0
  47. package/dist/config/agirailsmd.js +209 -0
  48. package/dist/config/agirailsmd.js.map +1 -0
  49. package/dist/config/networks.d.ts +22 -4
  50. package/dist/config/networks.d.ts.map +1 -1
  51. package/dist/config/networks.js +64 -26
  52. package/dist/config/networks.js.map +1 -1
  53. package/dist/config/publishPipeline.d.ts +75 -0
  54. package/dist/config/publishPipeline.d.ts.map +1 -0
  55. package/dist/config/publishPipeline.js +193 -0
  56. package/dist/config/publishPipeline.js.map +1 -0
  57. package/dist/config/syncOperations.d.ts +67 -0
  58. package/dist/config/syncOperations.d.ts.map +1 -0
  59. package/dist/config/syncOperations.js +208 -0
  60. package/dist/config/syncOperations.js.map +1 -0
  61. package/dist/erc8004/ERC8004Bridge.d.ts.map +1 -1
  62. package/dist/erc8004/ERC8004Bridge.js +6 -5
  63. package/dist/erc8004/ERC8004Bridge.js.map +1 -1
  64. package/dist/erc8004/ReputationReporter.d.ts.map +1 -1
  65. package/dist/erc8004/ReputationReporter.js +9 -12
  66. package/dist/erc8004/ReputationReporter.js.map +1 -1
  67. package/dist/index.d.ts +5 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +11 -3
  70. package/dist/index.js.map +1 -1
  71. package/dist/level0/request.d.ts.map +1 -1
  72. package/dist/level0/request.js +23 -86
  73. package/dist/level0/request.js.map +1 -1
  74. package/dist/level1/Agent.d.ts +0 -11
  75. package/dist/level1/Agent.d.ts.map +1 -1
  76. package/dist/level1/Agent.js +19 -36
  77. package/dist/level1/Agent.js.map +1 -1
  78. package/dist/protocol/ACTPKernel.d.ts +7 -1
  79. package/dist/protocol/ACTPKernel.d.ts.map +1 -1
  80. package/dist/protocol/ACTPKernel.js +13 -10
  81. package/dist/protocol/ACTPKernel.js.map +1 -1
  82. package/dist/protocol/EventMonitor.d.ts +14 -0
  83. package/dist/protocol/EventMonitor.d.ts.map +1 -1
  84. package/dist/protocol/EventMonitor.js +14 -0
  85. package/dist/protocol/EventMonitor.js.map +1 -1
  86. package/dist/registry/AgentRegistryClient.d.ts +75 -0
  87. package/dist/registry/AgentRegistryClient.d.ts.map +1 -0
  88. package/dist/registry/AgentRegistryClient.js +160 -0
  89. package/dist/registry/AgentRegistryClient.js.map +1 -0
  90. package/dist/runtime/BlockchainRuntime.d.ts +5 -0
  91. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  92. package/dist/runtime/BlockchainRuntime.js +1 -1
  93. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  94. package/dist/storage/ArchiveBundleBuilder.d.ts.map +1 -1
  95. package/dist/storage/ArchiveBundleBuilder.js.map +1 -1
  96. package/dist/storage/ArweaveClient.d.ts.map +1 -1
  97. package/dist/storage/ArweaveClient.js +2 -0
  98. package/dist/storage/ArweaveClient.js.map +1 -1
  99. package/dist/storage/FilebaseClient.d.ts.map +1 -1
  100. package/dist/storage/FilebaseClient.js +2 -0
  101. package/dist/storage/FilebaseClient.js.map +1 -1
  102. package/dist/types/adapter.d.ts +39 -0
  103. package/dist/types/adapter.d.ts.map +1 -1
  104. package/dist/types/adapter.js +7 -0
  105. package/dist/types/adapter.js.map +1 -1
  106. package/dist/types/x402.d.ts +23 -0
  107. package/dist/types/x402.d.ts.map +1 -1
  108. package/dist/types/x402.js.map +1 -1
  109. package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -1
  110. package/dist/utils/ErrorRecoveryGuide.js +3 -2
  111. package/dist/utils/ErrorRecoveryGuide.js.map +1 -1
  112. package/dist/utils/IPFSClient.d.ts +3 -2
  113. package/dist/utils/IPFSClient.d.ts.map +1 -1
  114. package/dist/utils/IPFSClient.js +7 -5
  115. package/dist/utils/IPFSClient.js.map +1 -1
  116. package/dist/utils/computeTypeHash.js +1 -3
  117. package/dist/utils/computeTypeHash.js.map +1 -1
  118. package/dist/utils/retry.d.ts.map +1 -1
  119. package/dist/utils/retry.js +0 -1
  120. package/dist/utils/retry.js.map +1 -1
  121. package/dist/utils/validation.d.ts +2 -2
  122. package/dist/utils/validation.d.ts.map +1 -1
  123. package/dist/utils/validation.js +2 -2
  124. package/dist/utils/validation.js.map +1 -1
  125. package/dist/wallet/AutoWalletProvider.d.ts +77 -0
  126. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -0
  127. package/dist/wallet/AutoWalletProvider.js +197 -0
  128. package/dist/wallet/AutoWalletProvider.js.map +1 -0
  129. package/dist/wallet/EOAWalletProvider.d.ts +21 -0
  130. package/dist/wallet/EOAWalletProvider.d.ts.map +1 -0
  131. package/dist/wallet/EOAWalletProvider.js +57 -0
  132. package/dist/wallet/EOAWalletProvider.js.map +1 -0
  133. package/dist/wallet/IWalletProvider.d.ts +115 -0
  134. package/dist/wallet/IWalletProvider.d.ts.map +1 -0
  135. package/dist/wallet/IWalletProvider.js +12 -0
  136. package/dist/wallet/IWalletProvider.js.map +1 -0
  137. package/dist/wallet/aa/BundlerClient.d.ts +70 -0
  138. package/dist/wallet/aa/BundlerClient.d.ts.map +1 -0
  139. package/dist/wallet/aa/BundlerClient.js +183 -0
  140. package/dist/wallet/aa/BundlerClient.js.map +1 -0
  141. package/dist/wallet/aa/DualNonceManager.d.ts +55 -0
  142. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -0
  143. package/dist/wallet/aa/DualNonceManager.js +131 -0
  144. package/dist/wallet/aa/DualNonceManager.js.map +1 -0
  145. package/dist/wallet/aa/PaymasterClient.d.ts +52 -0
  146. package/dist/wallet/aa/PaymasterClient.d.ts.map +1 -0
  147. package/dist/wallet/aa/PaymasterClient.js +115 -0
  148. package/dist/wallet/aa/PaymasterClient.js.map +1 -0
  149. package/dist/wallet/aa/TransactionBatcher.d.ts +87 -0
  150. package/dist/wallet/aa/TransactionBatcher.d.ts.map +1 -0
  151. package/dist/wallet/aa/TransactionBatcher.js +148 -0
  152. package/dist/wallet/aa/TransactionBatcher.js.map +1 -0
  153. package/dist/wallet/aa/UserOpBuilder.d.ts +71 -0
  154. package/dist/wallet/aa/UserOpBuilder.d.ts.map +1 -0
  155. package/dist/wallet/aa/UserOpBuilder.js +196 -0
  156. package/dist/wallet/aa/UserOpBuilder.js.map +1 -0
  157. package/dist/wallet/aa/constants.d.ts +54 -0
  158. package/dist/wallet/aa/constants.d.ts.map +1 -0
  159. package/dist/wallet/aa/constants.js +18 -0
  160. package/dist/wallet/aa/constants.js.map +1 -0
  161. package/dist/wallet/keystore.d.ts +16 -0
  162. package/dist/wallet/keystore.d.ts.map +1 -0
  163. package/dist/wallet/keystore.js +132 -0
  164. package/dist/wallet/keystore.js.map +1 -0
  165. package/package.json +5 -2
  166. package/src/ACTPClient.ts +275 -27
  167. package/src/abi/AgentRegistry.json +133 -0
  168. package/src/adapters/AdapterRouter.ts +0 -1
  169. package/src/adapters/BasicAdapter.ts +41 -1
  170. package/src/adapters/X402Adapter.ts +94 -16
  171. package/src/adapters/index.ts +9 -1
  172. package/src/cli/commands/diff.ts +141 -0
  173. package/src/cli/commands/init.ts +311 -22
  174. package/src/cli/commands/publish.ts +208 -0
  175. package/src/cli/commands/pull.ts +124 -0
  176. package/src/cli/commands/register.ts +233 -0
  177. package/src/cli/index.ts +12 -0
  178. package/src/cli/utils/config.ts +9 -0
  179. package/src/config/agirailsmd.ts +262 -0
  180. package/src/config/networks.ts +89 -26
  181. package/src/config/publishPipeline.ts +276 -0
  182. package/src/config/syncOperations.ts +279 -0
  183. package/src/erc8004/ERC8004Bridge.ts +6 -5
  184. package/src/erc8004/ReputationReporter.ts +14 -18
  185. package/src/index.ts +15 -0
  186. package/src/level0/request.ts +27 -88
  187. package/src/level1/Agent.ts +21 -37
  188. package/src/protocol/ACTPKernel.ts +20 -10
  189. package/src/protocol/EventMonitor.ts +14 -0
  190. package/src/registry/AgentRegistryClient.ts +202 -0
  191. package/src/runtime/BlockchainRuntime.ts +7 -1
  192. package/src/storage/ArchiveBundleBuilder.ts +0 -2
  193. package/src/storage/ArweaveClient.ts +2 -1
  194. package/src/storage/FilebaseClient.ts +3 -3
  195. package/src/types/adapter.ts +14 -0
  196. package/src/types/x402.ts +32 -0
  197. package/src/utils/ErrorRecoveryGuide.ts +4 -2
  198. package/src/utils/IPFSClient.ts +9 -7
  199. package/src/utils/computeTypeHash.ts +1 -3
  200. package/src/utils/retry.ts +0 -1
  201. package/src/utils/validation.ts +2 -2
  202. package/src/wallet/AutoWalletProvider.ts +294 -0
  203. package/src/wallet/EOAWalletProvider.ts +69 -0
  204. package/src/wallet/IWalletProvider.ts +133 -0
  205. package/src/wallet/aa/BundlerClient.ts +273 -0
  206. package/src/wallet/aa/DualNonceManager.ts +163 -0
  207. package/src/wallet/aa/PaymasterClient.ts +173 -0
  208. package/src/wallet/aa/TransactionBatcher.ts +240 -0
  209. package/src/wallet/aa/UserOpBuilder.ts +246 -0
  210. package/src/wallet/aa/constants.ts +60 -0
  211. package/src/wallet/keystore.ts +119 -0
@@ -10,6 +10,7 @@
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';
13
14
  import { Command } from 'commander';
14
15
  import {
15
16
  saveConfig,
@@ -31,6 +32,7 @@ export function createInitCommand(): Command {
31
32
  .description('Initialize ACTP in the current directory')
32
33
  .option('-m, --mode <mode>', 'Operating mode: mock, testnet, mainnet', 'mock')
33
34
  .option('-a, --address <address>', 'Your Ethereum address')
35
+ .option('-w, --wallet <type>', 'Wallet type: auto (gas-free Smart Wallet) or eoa (traditional)', 'auto')
34
36
  .option('-f, --force', 'Overwrite existing configuration')
35
37
  .option('--scaffold', 'Generate a starter agent.ts file')
36
38
  .option('--intent <intent>', 'Agent intent: earn, pay, or both (default: earn)')
@@ -66,6 +68,7 @@ type ScaffoldIntent = 'earn' | 'pay' | 'both';
66
68
  interface InitOptions {
67
69
  mode: string;
68
70
  address?: string;
71
+ wallet?: string;
69
72
  force?: boolean;
70
73
  scaffold?: boolean;
71
74
  intent?: string;
@@ -94,18 +97,45 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
94
97
 
95
98
  const mode = options.mode as CLIMode;
96
99
 
100
+ // Determine wallet type
101
+ const walletType = (mode === 'mock') ? 'mock' : (options.wallet || 'auto');
102
+ if (walletType !== 'mock' && walletType !== 'auto' && walletType !== 'eoa') {
103
+ throw new Error(
104
+ `Invalid wallet type: "${walletType}". Valid types: auto, eoa`
105
+ );
106
+ }
107
+
97
108
  // Get or generate address
98
109
  let address = options.address;
110
+ let smartWalletAddress: string | undefined;
111
+ let didRegister = false;
99
112
  if (!address) {
100
113
  if (mode === 'mock') {
101
114
  // Generate a random address for mock mode
102
115
  address = '0x' + crypto.randomBytes(20).toString('hex');
103
116
  output.info(`Generated mock address: ${address}`);
104
117
  } else {
105
- throw new Error(
106
- `Address required for ${mode} mode.\n` +
107
- 'Use --address <your-address> to specify.'
108
- );
118
+ // Generate a real wallet with encrypted keystore
119
+ const actpDir = getActpDir(projectRoot);
120
+ fs.mkdirSync(actpDir, { recursive: true });
121
+ const eoaAddress = await generateWallet(actpDir, output);
122
+
123
+ if (walletType === 'auto') {
124
+ // Compute Smart Wallet address from signer
125
+ smartWalletAddress = await computeSmartWalletInit(eoaAddress, mode, output);
126
+
127
+ // Y/N: Register for gas-free transactions?
128
+ const shouldRegister = await promptRegister(output);
129
+ if (shouldRegister) {
130
+ didRegister = await runInlineRegistration(projectRoot, mode, output);
131
+ }
132
+
133
+ // address = Smart Wallet if registered, EOA if not
134
+ // This ensures CLI commands (balance, tx list) show the correct address
135
+ address = didRegister ? smartWalletAddress : eoaAddress;
136
+ } else {
137
+ address = eoaAddress;
138
+ }
109
139
  }
110
140
  }
111
141
 
@@ -122,6 +152,9 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
122
152
  mode,
123
153
  address: address.toLowerCase(),
124
154
  version: '1.0',
155
+ ...(walletType !== 'mock' && { wallet: walletType as 'auto' | 'eoa' }),
156
+ ...(smartWalletAddress && { smartWallet: smartWalletAddress.toLowerCase() }),
157
+ ...(didRegister && { registered: true }),
125
158
  };
126
159
 
127
160
  // Save configuration
@@ -159,6 +192,7 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
159
192
  directory: getActpDir(projectRoot),
160
193
  mode,
161
194
  address,
195
+ ...(walletType !== 'mock' && { wallet: walletType }),
162
196
  },
163
197
  { quietKey: 'address' }
164
198
  );
@@ -169,14 +203,281 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
169
203
  } else {
170
204
  output.blank();
171
205
  output.print('Next steps:');
172
- output.print(' 1. Create a payment: actp pay <provider> <amount>');
173
- output.print(' 2. Check your balance: actp balance');
174
- output.print(' 3. List transactions: actp tx list');
206
+ if (walletType === 'auto' && didRegister) {
207
+ // Already registered ready to go
208
+ output.print(' 1. Create a payment: actp pay <provider> <amount>');
209
+ output.print(' 2. Check your balance: actp balance');
210
+ output.print(' 3. List transactions: actp tx list');
211
+ } else if (walletType === 'auto') {
212
+ // Skipped registration — remind them
213
+ output.print(' 1. Register for gas-free: actp register');
214
+ output.print(' 2. Create a payment: actp pay <provider> <amount>');
215
+ output.print(' 3. Check your balance: actp balance');
216
+ } else {
217
+ output.print(' 1. Create a payment: actp pay <provider> <amount>');
218
+ output.print(' 2. Check your balance: actp balance');
219
+ output.print(' 3. List transactions: actp tx list');
220
+ }
175
221
  output.print('');
176
222
  output.print('Tip: Use --scaffold to generate a starter agent.ts');
177
223
  }
178
224
  }
179
225
 
226
+ // ============================================================================
227
+ // Wallet Generation
228
+ // ============================================================================
229
+
230
+ async function generateWallet(actpDir: string, output: Output): Promise<string> {
231
+ const { Wallet } = await import('ethers');
232
+
233
+ const wallet = Wallet.createRandom();
234
+
235
+ // Get password from env var or interactive prompt
236
+ let password = process.env.ACTP_KEY_PASSWORD;
237
+ if (!password) {
238
+ password = await promptPassword();
239
+ }
240
+
241
+ if (!password || password.length < 8) {
242
+ throw new Error(
243
+ 'Wallet password required (minimum 8 characters).\n' +
244
+ 'Set ACTP_KEY_PASSWORD env var or enter when prompted.'
245
+ );
246
+ }
247
+
248
+ // Encrypt with Keystore V3 (scrypt + AES-128-CTR)
249
+ output.info('Encrypting wallet (this takes a few seconds)...');
250
+ const keystore = await wallet.encrypt(password);
251
+
252
+ // Save with restrictive permissions
253
+ const keystorePath = path.join(actpDir, 'keystore.json');
254
+ fs.writeFileSync(keystorePath, keystore, { mode: 0o600 });
255
+
256
+ output.success('Key securely saved and encrypted');
257
+ output.info(`Address: ${wallet.address}`);
258
+ output.warning('Back up your password — it cannot be recovered.');
259
+ output.info('');
260
+ output.info('To start your agent:');
261
+ output.info(' export ACTP_KEY_PASSWORD="your-password"');
262
+ output.info(' npx ts-node agent.ts');
263
+
264
+ return wallet.address;
265
+ }
266
+
267
+ /**
268
+ * Compute the Smart Wallet address for an EOA signer.
269
+ * Uses CREATE2 counterfactual derivation — no deployment needed.
270
+ */
271
+ async function computeSmartWalletInit(
272
+ eoaAddress: string,
273
+ mode: string,
274
+ output: Output
275
+ ): Promise<string> {
276
+ const { ethers } = await import('ethers');
277
+ const { getNetwork } = await import('../../config/networks');
278
+ const { computeSmartWalletAddress } = await import('../../wallet/aa/UserOpBuilder');
279
+
280
+ const network = mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
281
+ const networkConfig = getNetwork(network);
282
+ const rpcUrl = networkConfig.rpcUrl;
283
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
284
+
285
+ output.info('Computing Smart Wallet address...');
286
+ const smartWalletAddress = await computeSmartWalletAddress(eoaAddress, provider);
287
+
288
+ output.success(`Smart Wallet: ${smartWalletAddress}`);
289
+ output.info('Gas-free transactions enabled (requires registration)');
290
+ output.info('Register with: actp register');
291
+
292
+ return smartWalletAddress;
293
+ }
294
+
295
+ /**
296
+ * Ask user if they want to register for gas-free transactions.
297
+ * Non-TTY (piped/agent) defaults to yes.
298
+ */
299
+ async function promptRegister(output: Output): Promise<boolean> {
300
+ output.blank();
301
+ output.print('Register for gas-free transactions? (recommended)');
302
+ output.print(' Your agent gets a Smart Wallet with sponsored gas — no ETH needed.');
303
+ output.print(' Requires on-chain registration on AgentRegistry.');
304
+ output.blank();
305
+
306
+ if (!process.stdin.isTTY) {
307
+ output.info('Non-interactive mode: auto-registering');
308
+ return true;
309
+ }
310
+
311
+ const rl = readline.createInterface({
312
+ input: process.stdin,
313
+ output: process.stdout,
314
+ });
315
+
316
+ return new Promise((resolve) => {
317
+ rl.question(' Register now? [Y/n] ', (answer) => {
318
+ rl.close();
319
+ const trimmed = answer.trim().toLowerCase();
320
+ resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');
321
+ });
322
+ });
323
+ }
324
+
325
+ /**
326
+ * Run inline registration during init.
327
+ * Reuses the same logic as `actp register` — parses AGIRAILS.md,
328
+ * builds gasless UserOp (testnet: register + mint 1000 USDC).
329
+ *
330
+ * Returns true if registration succeeded, false on failure (non-fatal).
331
+ */
332
+ async function runInlineRegistration(
333
+ projectRoot: string,
334
+ mode: string,
335
+ output: Output
336
+ ): Promise<boolean> {
337
+ try {
338
+ const { resolvePrivateKey } = await import('../../wallet/keystore');
339
+ const privateKey = await resolvePrivateKey(projectRoot);
340
+ if (!privateKey) {
341
+ output.warning('Could not load wallet key. Run "actp register" later.');
342
+ return false;
343
+ }
344
+
345
+ const { parseAgirailsMd } = await import('../../config/agirailsmd');
346
+ const { extractRegistrationParams } = await import('../../config/publishPipeline');
347
+ const { ethers } = await import('ethers');
348
+ const { getNetwork } = await import('../../config/networks');
349
+ const { AutoWalletProvider } = await import('../../wallet/AutoWalletProvider');
350
+ const { buildRegisterAgentBatch, buildTestnetInitBatch } = await import('../../wallet/aa/TransactionBatcher');
351
+
352
+ // Parse AGIRAILS.md if present
353
+ const agirailsMdPath = path.join(projectRoot, 'AGIRAILS.md');
354
+ let endpoint = '';
355
+ let serviceDescriptors;
356
+
357
+ if (fs.existsSync(agirailsMdPath)) {
358
+ const content = fs.readFileSync(agirailsMdPath, 'utf-8');
359
+ const parsed = parseAgirailsMd(content);
360
+ const regParams = extractRegistrationParams(parsed.frontmatter);
361
+ endpoint = regParams.endpoint;
362
+ serviceDescriptors = regParams.serviceDescriptors;
363
+ output.info(`Parsed ${serviceDescriptors.length} service(s) from AGIRAILS.md`);
364
+ } else {
365
+ const serviceType = 'general';
366
+ serviceDescriptors = [{
367
+ serviceTypeHash: ethers.keccak256(ethers.toUtf8Bytes(serviceType)),
368
+ serviceType,
369
+ schemaURI: '',
370
+ minPrice: 0n,
371
+ maxPrice: 1_000_000_000n,
372
+ avgCompletionTime: 3600,
373
+ metadataCID: '',
374
+ }];
375
+ output.info('No AGIRAILS.md found. Using default "general" service.');
376
+ }
377
+
378
+ const network = mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
379
+ const networkConfig = getNetwork(network);
380
+
381
+ if (!networkConfig.aa || !networkConfig.contracts.agentRegistry) {
382
+ output.warning('AA or AgentRegistry not configured. Run "actp register" later.');
383
+ return false;
384
+ }
385
+
386
+ // Check for valid bundler/paymaster URL (CDP_API_KEY must be set)
387
+ const cdpUrl = networkConfig.aa.bundlerUrls.coinbase;
388
+ const hasPimlico = !!networkConfig.aa.bundlerUrls.pimlico;
389
+ if (cdpUrl.endsWith('/') && !hasPimlico) {
390
+ output.warning('CDP_API_KEY not set. Skipping registration.');
391
+ output.info('Set CDP_API_KEY and run "actp register" later.');
392
+ return false;
393
+ }
394
+
395
+ const provider = new ethers.JsonRpcProvider(networkConfig.rpcUrl);
396
+ const signer = new ethers.Wallet(privateKey, provider);
397
+
398
+ const autoWallet = await AutoWalletProvider.create({
399
+ signer,
400
+ provider,
401
+ chainId: networkConfig.chainId,
402
+ actpKernelAddress: networkConfig.contracts.actpKernel,
403
+ bundler: {
404
+ primaryUrl: networkConfig.aa.bundlerUrls.coinbase,
405
+ backupUrl: networkConfig.aa.bundlerUrls.pimlico,
406
+ },
407
+ paymaster: {
408
+ primaryUrl: networkConfig.aa.paymasterUrls.coinbase,
409
+ backupUrl: networkConfig.aa.paymasterUrls.pimlico,
410
+ },
411
+ });
412
+
413
+ const smartWalletAddress = autoWallet.getAddress();
414
+
415
+ // Build batch
416
+ let calls;
417
+ if (mode === 'testnet') {
418
+ output.info('Testnet: registering + minting 1000 test USDC in one gasless tx...');
419
+ calls = buildTestnetInitBatch({
420
+ agentRegistryAddress: networkConfig.contracts.agentRegistry,
421
+ endpoint,
422
+ serviceDescriptors,
423
+ mockUsdcAddress: networkConfig.contracts.usdc,
424
+ recipient: smartWalletAddress,
425
+ mintAmount: '1000000000',
426
+ });
427
+ } else {
428
+ output.info('Registering on AgentRegistry (gasless)...');
429
+ calls = buildRegisterAgentBatch(
430
+ networkConfig.contracts.agentRegistry,
431
+ endpoint,
432
+ serviceDescriptors
433
+ );
434
+ }
435
+
436
+ const txRequests = calls.map((c) => ({
437
+ to: c.target,
438
+ data: c.data,
439
+ value: c.value.toString(),
440
+ }));
441
+
442
+ const receipt = await autoWallet.sendBatchTransaction(txRequests);
443
+
444
+ if (!receipt.success) {
445
+ output.warning(`Registration failed (tx: ${receipt.hash}). Run "actp register" later.`);
446
+ return false;
447
+ }
448
+
449
+ output.success('Agent registered on AgentRegistry');
450
+ if (mode === 'testnet') {
451
+ output.success('Minted 1,000 test USDC to Smart Wallet');
452
+ }
453
+ output.print(` Tx: ${receipt.hash}`);
454
+ return true;
455
+ } catch (error) {
456
+ output.warning(`Registration failed: ${(error as Error).message}`);
457
+ output.info('You can register later with: actp register');
458
+ return false;
459
+ }
460
+ }
461
+
462
+ async function promptPassword(): Promise<string> {
463
+ // If not a TTY (e.g. piped or run by agent), skip prompt
464
+ if (!process.stdin.isTTY) {
465
+ return '';
466
+ }
467
+
468
+ const rl = readline.createInterface({
469
+ input: process.stdin,
470
+ output: process.stdout,
471
+ });
472
+
473
+ return new Promise((resolve) => {
474
+ rl.question('Enter password for wallet encryption (min 8 chars): ', (answer) => {
475
+ rl.close();
476
+ resolve(answer.trim());
477
+ });
478
+ });
479
+ }
480
+
180
481
  // ============================================================================
181
482
  // Scaffold
182
483
  // ============================================================================
@@ -236,8 +537,8 @@ async function runScaffold(
236
537
  const tsconfigContent = JSON.stringify({
237
538
  compilerOptions: {
238
539
  target: 'ES2022',
239
- module: 'ES2022',
240
- moduleResolution: 'bundler',
540
+ module: 'commonjs',
541
+ moduleResolution: 'node',
241
542
  esModuleInterop: true,
242
543
  strict: true,
243
544
  outDir: 'dist',
@@ -256,19 +557,7 @@ async function runScaffold(
256
557
  }
257
558
  }
258
559
 
259
- // Check package.json for type: module
260
- const pkgFile = path.join(process.cwd(), 'package.json');
261
- if (fs.existsSync(pkgFile)) {
262
- try {
263
- const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
264
- if (pkg.type !== 'module') {
265
- output.warning(
266
- 'package.json has type: "' + (pkg.type || 'commonjs') + '". ' +
267
- 'Set "type": "module" for ESM support, or run with: npx ts-node --esm agent.ts'
268
- );
269
- }
270
- } catch { /* ignore parse errors */ }
271
- }
560
+ // Note: @agirails/sdk is CJS — no type:module check needed
272
561
 
273
562
  output.blank();
274
563
  output.print('Next steps:');
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Publish Command - Publish AGIRAILS.md config on-chain
3
+ *
4
+ * Reads AGIRAILS.md, computes canonical hash, uploads to IPFS,
5
+ * optionally to Arweave, and records on AgentRegistry.
6
+ *
7
+ * @module cli/commands/publish
8
+ */
9
+
10
+ import { Command } from 'commander';
11
+ import { Output, ExitCode } from '../utils/output';
12
+ import { mapError } from '../utils/client';
13
+ import { resolve } from 'path';
14
+ import { readFileSync, existsSync } from 'fs';
15
+ import { ethers } from 'ethers';
16
+ import { computeConfigHash, parseAgirailsMd } from '../../config/agirailsmd';
17
+ import { FilebaseClient } from '../../storage/FilebaseClient';
18
+ import { ArweaveClient } from '../../storage/ArweaveClient';
19
+ import { getNetwork } from '../../config/networks';
20
+ import { publishAgirailsMd, PENDING_ENDPOINT } from '../../config/publishPipeline';
21
+
22
+ // ============================================================================
23
+ // Command Definition
24
+ // ============================================================================
25
+
26
+ export function createPublishCommand(): Command {
27
+ const cmd = new Command('publish')
28
+ .description('Publish AGIRAILS.md config on-chain')
29
+ .argument('[path]', 'Path to AGIRAILS.md', './AGIRAILS.md')
30
+ .option('-n, --network <network>', 'Network (base-sepolia | base-mainnet)', 'base-sepolia')
31
+ .option('--skip-arweave', 'Skip permanent Arweave storage (dev mode)')
32
+ .option('--dry-run', 'Show what would happen without executing')
33
+ .option('--json', 'Output as JSON')
34
+ .option('-q, --quiet', 'Output only the config hash')
35
+ .action(async (path, options) => {
36
+ const output = new Output(
37
+ options.json ? 'json' : options.quiet ? 'quiet' : 'human'
38
+ );
39
+
40
+ try {
41
+ await runPublish(path, options, output);
42
+ } catch (error) {
43
+ const structuredError = mapError(error);
44
+ output.errorResult({
45
+ code: structuredError.code,
46
+ message: structuredError.message,
47
+ details: structuredError.details,
48
+ });
49
+ process.exit(ExitCode.ERROR);
50
+ }
51
+ });
52
+
53
+ return cmd;
54
+ }
55
+
56
+ // ============================================================================
57
+ // Implementation
58
+ // ============================================================================
59
+
60
+ interface PublishCommandOptions {
61
+ network: string;
62
+ skipArweave?: boolean;
63
+ dryRun?: boolean;
64
+ }
65
+
66
+ async function runPublish(
67
+ filePath: string,
68
+ options: PublishCommandOptions,
69
+ output: Output
70
+ ): Promise<void> {
71
+ const resolvedPath = resolve(filePath);
72
+
73
+ if (!existsSync(resolvedPath)) {
74
+ output.error(`File not found: ${filePath}`);
75
+ process.exit(ExitCode.INVALID_INPUT);
76
+ }
77
+
78
+ const spinner = output.spinner('Reading AGIRAILS.md...');
79
+
80
+ try {
81
+ // Read and compute hash
82
+ const content = readFileSync(resolvedPath, 'utf-8');
83
+ const { configHash, structuredHash, bodyHash } = computeConfigHash(content);
84
+
85
+ if (options.dryRun) {
86
+ spinner.stop(true);
87
+
88
+ output.result(
89
+ {
90
+ configHash,
91
+ structuredHash,
92
+ bodyHash,
93
+ path: resolvedPath,
94
+ network: options.network,
95
+ dryRun: true,
96
+ },
97
+ { quietKey: 'configHash' }
98
+ );
99
+
100
+ output.blank();
101
+ output.success('Dry run complete. No changes made.');
102
+ return;
103
+ }
104
+
105
+ // Validate environment
106
+ const privateKey = process.env.ACTP_PRIVATE_KEY || process.env.PRIVATE_KEY;
107
+ if (!privateKey) {
108
+ spinner.stop(false);
109
+ output.error('Private key required. Set ACTP_PRIVATE_KEY or PRIVATE_KEY env var.');
110
+ process.exit(ExitCode.INVALID_INPUT);
111
+ }
112
+
113
+ const networkConfig = getNetwork(options.network);
114
+ if (!networkConfig.contracts.agentRegistry) {
115
+ spinner.stop(false);
116
+ output.error(`AgentRegistry not deployed on ${options.network}`);
117
+ process.exit(ExitCode.ERROR);
118
+ }
119
+
120
+ // Create provider and signer
121
+ const provider = new ethers.JsonRpcProvider(networkConfig.rpcUrl);
122
+ const signer = new ethers.Wallet(privateKey, provider);
123
+
124
+ // Create Filebase client
125
+ const filebaseAccessKey = process.env.FILEBASE_ACCESS_KEY;
126
+ const filebaseSecretKey = process.env.FILEBASE_SECRET_KEY;
127
+ if (!filebaseAccessKey || !filebaseSecretKey) {
128
+ spinner.stop(false);
129
+ output.error('Filebase credentials required. Set FILEBASE_ACCESS_KEY and FILEBASE_SECRET_KEY.');
130
+ process.exit(ExitCode.INVALID_INPUT);
131
+ }
132
+
133
+ const filebaseClient = new FilebaseClient({
134
+ accessKey: filebaseAccessKey,
135
+ secretKey: filebaseSecretKey,
136
+ });
137
+
138
+ // Create Arweave client (optional)
139
+ let arweaveClient: ArweaveClient | undefined;
140
+ if (!options.skipArweave) {
141
+ const arweaveKey = process.env.ARCHIVE_UPLOADER_KEY;
142
+ if (arweaveKey) {
143
+ arweaveClient = await ArweaveClient.create({
144
+ privateKey: arweaveKey,
145
+ rpcUrl: networkConfig.rpcUrl,
146
+ });
147
+ }
148
+ }
149
+
150
+ spinner.stop(true);
151
+ const publishSpinner = output.spinner('Publishing to IPFS + on-chain...');
152
+
153
+ const result = await publishAgirailsMd({
154
+ path: resolvedPath,
155
+ network: options.network,
156
+ registryAddress: networkConfig.contracts.agentRegistry,
157
+ signer,
158
+ filebaseClient,
159
+ arweaveClient,
160
+ skipArweave: options.skipArweave || !arweaveClient,
161
+ gasSettings: {
162
+ maxFeePerGas: networkConfig.gasSettings.maxFeePerGas,
163
+ maxPriorityFeePerGas: networkConfig.gasSettings.maxPriorityFeePerGas,
164
+ },
165
+ });
166
+
167
+ publishSpinner.stop(true);
168
+
169
+ output.result(
170
+ {
171
+ configHash: result.configHash,
172
+ cid: result.cid,
173
+ txHash: result.txHash,
174
+ arweaveTxId: result.arweaveTxId || null,
175
+ registered: result.registered || false,
176
+ network: options.network,
177
+ },
178
+ { quietKey: 'configHash' }
179
+ );
180
+
181
+ output.blank();
182
+ if (result.registered) {
183
+ output.success('Agent registered and config published!');
184
+ } else {
185
+ output.success('Config published successfully!');
186
+ }
187
+ output.print('');
188
+ output.print('Next steps:');
189
+ output.print(' - Verify sync: actp diff');
190
+ output.print(' - View on-chain: ' + networkConfig.blockExplorer + '/tx/' + result.txHash);
191
+
192
+ // Warn if placeholder endpoint was used during auto-register
193
+ if (result.registered) {
194
+ const content = readFileSync(resolvedPath, 'utf-8');
195
+ const { frontmatter } = parseAgirailsMd(content);
196
+ if (!frontmatter.endpoint || frontmatter.endpoint === PENDING_ENDPOINT) {
197
+ output.print('');
198
+ output.warning('No endpoint in AGIRAILS.md — registered with placeholder URL.');
199
+ output.print(' Update when your agent is deployed:');
200
+ output.print(' 1. Add "endpoint: https://your-agent.com/webhook" to AGIRAILS.md');
201
+ output.print(' 2. Run: actp publish');
202
+ }
203
+ }
204
+ } catch (error) {
205
+ spinner.stop(false);
206
+ throw error;
207
+ }
208
+ }