@agirails/sdk 2.3.0 → 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.
- package/README.md +45 -8
- package/dist/ACTPClient.d.ts +35 -1
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +156 -26
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/AdapterRouter.d.ts.map +1 -1
- package/dist/adapters/AdapterRouter.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +10 -1
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +36 -1
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +210 -18
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/publish.d.ts.map +1 -1
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.d.ts +16 -0
- package/dist/cli/commands/register.d.ts.map +1 -0
- package/dist/cli/commands/register.js +211 -0
- package/dist/cli/commands/register.js.map +1 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/config.d.ts +6 -0
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/config/networks.d.ts +20 -4
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +59 -27
- package/dist/config/networks.js.map +1 -1
- package/dist/config/publishPipeline.d.ts +14 -0
- package/dist/config/publishPipeline.d.ts.map +1 -1
- package/dist/config/publishPipeline.js +2 -1
- package/dist/config/publishPipeline.js.map +1 -1
- package/dist/erc8004/ERC8004Bridge.d.ts.map +1 -1
- package/dist/erc8004/ERC8004Bridge.js +6 -5
- package/dist/erc8004/ERC8004Bridge.js.map +1 -1
- package/dist/erc8004/ReputationReporter.d.ts.map +1 -1
- package/dist/erc8004/ReputationReporter.js +9 -12
- package/dist/erc8004/ReputationReporter.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/level1/Agent.js +4 -4
- package/dist/level1/Agent.js.map +1 -1
- package/dist/protocol/ACTPKernel.d.ts +7 -1
- package/dist/protocol/ACTPKernel.d.ts.map +1 -1
- package/dist/protocol/ACTPKernel.js +13 -10
- package/dist/protocol/ACTPKernel.js.map +1 -1
- package/dist/protocol/EventMonitor.d.ts +14 -0
- package/dist/protocol/EventMonitor.d.ts.map +1 -1
- package/dist/protocol/EventMonitor.js +14 -0
- package/dist/protocol/EventMonitor.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts +5 -0
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +1 -1
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/storage/ArchiveBundleBuilder.d.ts.map +1 -1
- package/dist/storage/ArchiveBundleBuilder.js.map +1 -1
- package/dist/storage/ArweaveClient.d.ts.map +1 -1
- package/dist/storage/ArweaveClient.js +2 -0
- package/dist/storage/ArweaveClient.js.map +1 -1
- package/dist/storage/FilebaseClient.d.ts.map +1 -1
- package/dist/storage/FilebaseClient.js +2 -0
- package/dist/storage/FilebaseClient.js.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.js +3 -2
- package/dist/utils/ErrorRecoveryGuide.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -2
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +7 -5
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/utils/computeTypeHash.js +1 -3
- package/dist/utils/computeTypeHash.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +0 -1
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/validation.d.ts +2 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +2 -2
- package/dist/utils/validation.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +77 -0
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -0
- package/dist/wallet/AutoWalletProvider.js +197 -0
- package/dist/wallet/AutoWalletProvider.js.map +1 -0
- package/dist/wallet/EOAWalletProvider.d.ts +21 -0
- package/dist/wallet/EOAWalletProvider.d.ts.map +1 -0
- package/dist/wallet/EOAWalletProvider.js +57 -0
- package/dist/wallet/EOAWalletProvider.js.map +1 -0
- package/dist/wallet/IWalletProvider.d.ts +115 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -0
- package/dist/wallet/IWalletProvider.js +12 -0
- package/dist/wallet/IWalletProvider.js.map +1 -0
- package/dist/wallet/aa/BundlerClient.d.ts +70 -0
- package/dist/wallet/aa/BundlerClient.d.ts.map +1 -0
- package/dist/wallet/aa/BundlerClient.js +183 -0
- package/dist/wallet/aa/BundlerClient.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +55 -0
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -0
- package/dist/wallet/aa/DualNonceManager.js +131 -0
- package/dist/wallet/aa/DualNonceManager.js.map +1 -0
- package/dist/wallet/aa/PaymasterClient.d.ts +52 -0
- package/dist/wallet/aa/PaymasterClient.d.ts.map +1 -0
- package/dist/wallet/aa/PaymasterClient.js +115 -0
- package/dist/wallet/aa/PaymasterClient.js.map +1 -0
- package/dist/wallet/aa/TransactionBatcher.d.ts +87 -0
- package/dist/wallet/aa/TransactionBatcher.d.ts.map +1 -0
- package/dist/wallet/aa/TransactionBatcher.js +148 -0
- package/dist/wallet/aa/TransactionBatcher.js.map +1 -0
- package/dist/wallet/aa/UserOpBuilder.d.ts +71 -0
- package/dist/wallet/aa/UserOpBuilder.d.ts.map +1 -0
- package/dist/wallet/aa/UserOpBuilder.js +196 -0
- package/dist/wallet/aa/UserOpBuilder.js.map +1 -0
- package/dist/wallet/aa/constants.d.ts +54 -0
- package/dist/wallet/aa/constants.d.ts.map +1 -0
- package/dist/wallet/aa/constants.js +18 -0
- package/dist/wallet/aa/constants.js.map +1 -0
- package/package.json +4 -2
- package/src/ACTPClient.ts +217 -31
- package/src/adapters/AdapterRouter.ts +0 -1
- package/src/adapters/BasicAdapter.ts +41 -1
- package/src/cli/commands/init.ts +247 -19
- package/src/cli/commands/publish.ts +1 -2
- package/src/cli/commands/register.ts +233 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/utils/config.ts +9 -0
- package/src/config/networks.ts +82 -27
- package/src/config/publishPipeline.ts +2 -2
- package/src/erc8004/ERC8004Bridge.ts +6 -5
- package/src/erc8004/ReputationReporter.ts +14 -18
- package/src/index.ts +12 -0
- package/src/level1/Agent.ts +5 -5
- package/src/protocol/ACTPKernel.ts +20 -10
- package/src/protocol/EventMonitor.ts +14 -0
- package/src/runtime/BlockchainRuntime.ts +7 -1
- package/src/storage/ArchiveBundleBuilder.ts +0 -2
- package/src/storage/ArweaveClient.ts +2 -1
- package/src/storage/FilebaseClient.ts +3 -3
- package/src/utils/ErrorRecoveryGuide.ts +4 -2
- package/src/utils/IPFSClient.ts +9 -7
- package/src/utils/computeTypeHash.ts +1 -3
- package/src/utils/retry.ts +0 -1
- package/src/utils/validation.ts +2 -2
- package/src/wallet/AutoWalletProvider.ts +294 -0
- package/src/wallet/EOAWalletProvider.ts +69 -0
- package/src/wallet/IWalletProvider.ts +133 -0
- package/src/wallet/aa/BundlerClient.ts +273 -0
- package/src/wallet/aa/DualNonceManager.ts +163 -0
- package/src/wallet/aa/PaymasterClient.ts +173 -0
- package/src/wallet/aa/TransactionBatcher.ts +240 -0
- package/src/wallet/aa/UserOpBuilder.ts +246 -0
- package/src/wallet/aa/constants.ts +60 -0
package/src/cli/commands/init.ts
CHANGED
|
@@ -32,6 +32,7 @@ export function createInitCommand(): Command {
|
|
|
32
32
|
.description('Initialize ACTP in the current directory')
|
|
33
33
|
.option('-m, --mode <mode>', 'Operating mode: mock, testnet, mainnet', 'mock')
|
|
34
34
|
.option('-a, --address <address>', 'Your Ethereum address')
|
|
35
|
+
.option('-w, --wallet <type>', 'Wallet type: auto (gas-free Smart Wallet) or eoa (traditional)', 'auto')
|
|
35
36
|
.option('-f, --force', 'Overwrite existing configuration')
|
|
36
37
|
.option('--scaffold', 'Generate a starter agent.ts file')
|
|
37
38
|
.option('--intent <intent>', 'Agent intent: earn, pay, or both (default: earn)')
|
|
@@ -67,6 +68,7 @@ type ScaffoldIntent = 'earn' | 'pay' | 'both';
|
|
|
67
68
|
interface InitOptions {
|
|
68
69
|
mode: string;
|
|
69
70
|
address?: string;
|
|
71
|
+
wallet?: string;
|
|
70
72
|
force?: boolean;
|
|
71
73
|
scaffold?: boolean;
|
|
72
74
|
intent?: string;
|
|
@@ -95,8 +97,18 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
|
|
|
95
97
|
|
|
96
98
|
const mode = options.mode as CLIMode;
|
|
97
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
|
+
|
|
98
108
|
// Get or generate address
|
|
99
109
|
let address = options.address;
|
|
110
|
+
let smartWalletAddress: string | undefined;
|
|
111
|
+
let didRegister = false;
|
|
100
112
|
if (!address) {
|
|
101
113
|
if (mode === 'mock') {
|
|
102
114
|
// Generate a random address for mock mode
|
|
@@ -106,7 +118,24 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
|
|
|
106
118
|
// Generate a real wallet with encrypted keystore
|
|
107
119
|
const actpDir = getActpDir(projectRoot);
|
|
108
120
|
fs.mkdirSync(actpDir, { recursive: true });
|
|
109
|
-
|
|
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
|
+
}
|
|
110
139
|
}
|
|
111
140
|
}
|
|
112
141
|
|
|
@@ -123,6 +152,9 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
|
|
|
123
152
|
mode,
|
|
124
153
|
address: address.toLowerCase(),
|
|
125
154
|
version: '1.0',
|
|
155
|
+
...(walletType !== 'mock' && { wallet: walletType as 'auto' | 'eoa' }),
|
|
156
|
+
...(smartWalletAddress && { smartWallet: smartWalletAddress.toLowerCase() }),
|
|
157
|
+
...(didRegister && { registered: true }),
|
|
126
158
|
};
|
|
127
159
|
|
|
128
160
|
// Save configuration
|
|
@@ -160,6 +192,7 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
|
|
|
160
192
|
directory: getActpDir(projectRoot),
|
|
161
193
|
mode,
|
|
162
194
|
address,
|
|
195
|
+
...(walletType !== 'mock' && { wallet: walletType }),
|
|
163
196
|
},
|
|
164
197
|
{ quietKey: 'address' }
|
|
165
198
|
);
|
|
@@ -170,9 +203,21 @@ async function runInit(options: InitOptions, output: Output): Promise<void> {
|
|
|
170
203
|
} else {
|
|
171
204
|
output.blank();
|
|
172
205
|
output.print('Next steps:');
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
+
}
|
|
176
221
|
output.print('');
|
|
177
222
|
output.print('Tip: Use --scaffold to generate a starter agent.ts');
|
|
178
223
|
}
|
|
@@ -219,6 +264,201 @@ async function generateWallet(actpDir: string, output: Output): Promise<string>
|
|
|
219
264
|
return wallet.address;
|
|
220
265
|
}
|
|
221
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
|
+
|
|
222
462
|
async function promptPassword(): Promise<string> {
|
|
223
463
|
// If not a TTY (e.g. piped or run by agent), skip prompt
|
|
224
464
|
if (!process.stdin.isTTY) {
|
|
@@ -297,8 +537,8 @@ async function runScaffold(
|
|
|
297
537
|
const tsconfigContent = JSON.stringify({
|
|
298
538
|
compilerOptions: {
|
|
299
539
|
target: 'ES2022',
|
|
300
|
-
module: '
|
|
301
|
-
moduleResolution: '
|
|
540
|
+
module: 'commonjs',
|
|
541
|
+
moduleResolution: 'node',
|
|
302
542
|
esModuleInterop: true,
|
|
303
543
|
strict: true,
|
|
304
544
|
outDir: 'dist',
|
|
@@ -317,19 +557,7 @@ async function runScaffold(
|
|
|
317
557
|
}
|
|
318
558
|
}
|
|
319
559
|
|
|
320
|
-
//
|
|
321
|
-
const pkgFile = path.join(process.cwd(), 'package.json');
|
|
322
|
-
if (fs.existsSync(pkgFile)) {
|
|
323
|
-
try {
|
|
324
|
-
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
325
|
-
if (pkg.type !== 'module') {
|
|
326
|
-
output.warning(
|
|
327
|
-
'package.json has type: "' + (pkg.type || 'commonjs') + '". ' +
|
|
328
|
-
'Set "type": "module" for ESM support, or run with: npx ts-node --esm agent.ts'
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
} catch { /* ignore parse errors */ }
|
|
332
|
-
}
|
|
560
|
+
// Note: @agirails/sdk is CJS — no type:module check needed
|
|
333
561
|
|
|
334
562
|
output.blank();
|
|
335
563
|
output.print('Next steps:');
|
|
@@ -13,8 +13,7 @@ import { mapError } from '../utils/client';
|
|
|
13
13
|
import { resolve } from 'path';
|
|
14
14
|
import { readFileSync, existsSync } from 'fs';
|
|
15
15
|
import { ethers } from 'ethers';
|
|
16
|
-
import { computeConfigHash, parseAgirailsMd
|
|
17
|
-
import { AgentRegistryClient } from '../../registry/AgentRegistryClient';
|
|
16
|
+
import { computeConfigHash, parseAgirailsMd } from '../../config/agirailsmd';
|
|
18
17
|
import { FilebaseClient } from '../../storage/FilebaseClient';
|
|
19
18
|
import { ArweaveClient } from '../../storage/ArweaveClient';
|
|
20
19
|
import { getNetwork } from '../../config/networks';
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Command — Register agent on AgentRegistry for gas-free transactions.
|
|
3
|
+
*
|
|
4
|
+
* Parses AGIRAILS.md for service descriptors and endpoint, then registers
|
|
5
|
+
* on-chain via a single gasless UserOp (bootstrap-allowed).
|
|
6
|
+
*
|
|
7
|
+
* For testnet: also mints 1000 test USDC in the same UserOp.
|
|
8
|
+
*
|
|
9
|
+
* WARNING: This creates a NEW Smart Wallet address (different from EOA).
|
|
10
|
+
* Old EOA reputation/history does not transfer.
|
|
11
|
+
*
|
|
12
|
+
* @module cli/commands/register
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import { Output, ExitCode } from '../utils/output';
|
|
19
|
+
import { loadConfig, updateConfig } from '../utils/config';
|
|
20
|
+
import { resolvePrivateKey } from '../../wallet/keystore';
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Command Definition
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
export function createRegisterCommand(): Command {
|
|
27
|
+
const cmd = new Command('register')
|
|
28
|
+
.description('Register agent on AgentRegistry for gas-free transactions')
|
|
29
|
+
.option('--endpoint <url>', 'Service endpoint URL (overrides AGIRAILS.md)')
|
|
30
|
+
.option('--json', 'Output as JSON')
|
|
31
|
+
.option('-q, --quiet', 'Minimal output')
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
const output = new Output(
|
|
34
|
+
options.json ? 'json' : options.quiet ? 'quiet' : 'human'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await runRegister(options, output);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
output.errorResult({
|
|
41
|
+
code: 'REGISTER_FAILED',
|
|
42
|
+
message: (error as Error).message,
|
|
43
|
+
});
|
|
44
|
+
process.exit(ExitCode.ERROR);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return cmd;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Implementation
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
async function runRegister(
|
|
56
|
+
options: { endpoint?: string },
|
|
57
|
+
output: Output
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
const projectRoot = process.cwd();
|
|
60
|
+
|
|
61
|
+
// Load config
|
|
62
|
+
const config = loadConfig(projectRoot);
|
|
63
|
+
if (!config) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'ACTP not initialized. Run "actp init" first.'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (config.mode === 'mock') {
|
|
70
|
+
throw new Error('Registration is not available in mock mode.');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Resolve private key
|
|
74
|
+
const privateKey = await resolvePrivateKey(projectRoot);
|
|
75
|
+
if (!privateKey) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
'No wallet found. Run "actp init" first to generate a wallet.'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Parse AGIRAILS.md for service descriptors
|
|
82
|
+
const { parseAgirailsMd } = await import('../../config/agirailsmd');
|
|
83
|
+
const { extractRegistrationParams } = await import('../../config/publishPipeline');
|
|
84
|
+
|
|
85
|
+
const agirailsMdPath = path.join(projectRoot, 'AGIRAILS.md');
|
|
86
|
+
let endpoint = options.endpoint || '';
|
|
87
|
+
let serviceDescriptors;
|
|
88
|
+
|
|
89
|
+
if (fs.existsSync(agirailsMdPath)) {
|
|
90
|
+
const content = fs.readFileSync(agirailsMdPath, 'utf-8');
|
|
91
|
+
const parsed = parseAgirailsMd(content);
|
|
92
|
+
const regParams = extractRegistrationParams(parsed.frontmatter);
|
|
93
|
+
endpoint = options.endpoint || regParams.endpoint;
|
|
94
|
+
serviceDescriptors = regParams.serviceDescriptors;
|
|
95
|
+
output.info(`Parsed ${serviceDescriptors.length} service(s) from AGIRAILS.md`);
|
|
96
|
+
} else {
|
|
97
|
+
// No AGIRAILS.md — use minimal defaults
|
|
98
|
+
const { ethers: ethersLib } = await import('ethers');
|
|
99
|
+
const serviceType = 'general';
|
|
100
|
+
serviceDescriptors = [{
|
|
101
|
+
serviceTypeHash: ethersLib.keccak256(ethersLib.toUtf8Bytes(serviceType)),
|
|
102
|
+
serviceType,
|
|
103
|
+
schemaURI: '',
|
|
104
|
+
minPrice: 0n,
|
|
105
|
+
maxPrice: 1_000_000_000n, // 1000 USDC
|
|
106
|
+
avgCompletionTime: 3600,
|
|
107
|
+
metadataCID: '',
|
|
108
|
+
}];
|
|
109
|
+
output.warning('No AGIRAILS.md found. Using default "general" service descriptor.');
|
|
110
|
+
output.info('Create AGIRAILS.md with services to customize registration.');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Dynamic imports
|
|
114
|
+
const { ethers } = await import('ethers');
|
|
115
|
+
const { getNetwork } = await import('../../config/networks');
|
|
116
|
+
const { AutoWalletProvider } = await import('../../wallet/AutoWalletProvider');
|
|
117
|
+
const { buildRegisterAgentBatch, buildTestnetInitBatch } = await import('../../wallet/aa/TransactionBatcher');
|
|
118
|
+
|
|
119
|
+
const network = config.mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
|
|
120
|
+
const networkConfig = getNetwork(network);
|
|
121
|
+
|
|
122
|
+
if (!networkConfig.aa) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`AA configuration not available for ${config.mode}. ` +
|
|
125
|
+
'Smart Wallet registration requires AA support.'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!networkConfig.contracts.agentRegistry) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
'AgentRegistry contract not deployed yet. Registration not available.'
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const rpcUrl = networkConfig.rpcUrl;
|
|
136
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
137
|
+
const signer = new ethers.Wallet(privateKey, provider);
|
|
138
|
+
|
|
139
|
+
output.info('Creating Smart Wallet...');
|
|
140
|
+
|
|
141
|
+
const autoWallet = await AutoWalletProvider.create({
|
|
142
|
+
signer,
|
|
143
|
+
provider,
|
|
144
|
+
chainId: networkConfig.chainId,
|
|
145
|
+
actpKernelAddress: networkConfig.contracts.actpKernel,
|
|
146
|
+
bundler: {
|
|
147
|
+
primaryUrl: networkConfig.aa.bundlerUrls.coinbase,
|
|
148
|
+
backupUrl: networkConfig.aa.bundlerUrls.pimlico,
|
|
149
|
+
},
|
|
150
|
+
paymaster: {
|
|
151
|
+
primaryUrl: networkConfig.aa.paymasterUrls.coinbase,
|
|
152
|
+
backupUrl: networkConfig.aa.paymasterUrls.pimlico,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const smartWalletAddress = autoWallet.getAddress();
|
|
157
|
+
output.info(`Smart Wallet: ${smartWalletAddress}`);
|
|
158
|
+
|
|
159
|
+
if (smartWalletAddress.toLowerCase() !== config.address.toLowerCase()) {
|
|
160
|
+
output.warning(
|
|
161
|
+
`This creates a NEW address: ${smartWalletAddress}\n` +
|
|
162
|
+
` Your current address: ${config.address}\n` +
|
|
163
|
+
' Old address reputation/history does NOT transfer.'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Build batch — testnet gets register + mint, mainnet gets register only
|
|
168
|
+
let calls;
|
|
169
|
+
if (config.mode === 'testnet') {
|
|
170
|
+
output.info('Testnet mode: will register + mint 1000 test USDC in one UserOp');
|
|
171
|
+
calls = buildTestnetInitBatch({
|
|
172
|
+
agentRegistryAddress: networkConfig.contracts.agentRegistry,
|
|
173
|
+
endpoint,
|
|
174
|
+
serviceDescriptors,
|
|
175
|
+
mockUsdcAddress: networkConfig.contracts.usdc,
|
|
176
|
+
recipient: smartWalletAddress,
|
|
177
|
+
mintAmount: '1000000000', // 1000 USDC
|
|
178
|
+
});
|
|
179
|
+
} else {
|
|
180
|
+
calls = buildRegisterAgentBatch(
|
|
181
|
+
networkConfig.contracts.agentRegistry,
|
|
182
|
+
endpoint,
|
|
183
|
+
serviceDescriptors
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Convert SmartWalletCall[] to TransactionRequest[]
|
|
188
|
+
const txRequests = calls.map((c) => ({
|
|
189
|
+
to: c.target,
|
|
190
|
+
data: c.data,
|
|
191
|
+
value: c.value.toString(),
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
output.info('Submitting registration (gasless UserOp)...');
|
|
195
|
+
|
|
196
|
+
const receipt = await autoWallet.sendBatchTransaction(txRequests);
|
|
197
|
+
|
|
198
|
+
if (!receipt.success) {
|
|
199
|
+
throw new Error(`Registration UserOp failed: ${receipt.hash}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
output.success('Agent registered on AgentRegistry');
|
|
203
|
+
if (config.mode === 'testnet') {
|
|
204
|
+
output.success('Minted 1,000 test USDC to Smart Wallet');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Update config: flip address to Smart Wallet, mark as registered
|
|
208
|
+
updateConfig({
|
|
209
|
+
address: smartWalletAddress.toLowerCase(),
|
|
210
|
+
smartWallet: smartWalletAddress.toLowerCase(),
|
|
211
|
+
registered: true,
|
|
212
|
+
}, projectRoot);
|
|
213
|
+
output.info('Config updated: address set to Smart Wallet');
|
|
214
|
+
|
|
215
|
+
output.blank();
|
|
216
|
+
output.result(
|
|
217
|
+
{
|
|
218
|
+
registered: true,
|
|
219
|
+
smartWallet: smartWalletAddress,
|
|
220
|
+
services: serviceDescriptors.length,
|
|
221
|
+
txHash: receipt.hash,
|
|
222
|
+
...(config.mode === 'testnet' && { mintedUSDC: '1000' }),
|
|
223
|
+
},
|
|
224
|
+
{ quietKey: 'smartWallet' }
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
output.blank();
|
|
228
|
+
output.print('Your agent now has gas-free transactions!');
|
|
229
|
+
output.print(`Smart Wallet address: ${smartWalletAddress}`);
|
|
230
|
+
output.print('');
|
|
231
|
+
output.print('To use gas-free mode, add to your agent config:');
|
|
232
|
+
output.print(' wallet: "auto"');
|
|
233
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ import { createTimeCommand } from './commands/time';
|
|
|
51
51
|
import { createPublishCommand } from './commands/publish';
|
|
52
52
|
import { createPullCommand } from './commands/pull';
|
|
53
53
|
import { createDiffCommand } from './commands/diff';
|
|
54
|
+
import { createRegisterCommand } from './commands/register';
|
|
54
55
|
|
|
55
56
|
// ============================================================================
|
|
56
57
|
// Program Setup
|
|
@@ -100,6 +101,9 @@ program.addCommand(createPublishCommand());
|
|
|
100
101
|
program.addCommand(createPullCommand());
|
|
101
102
|
program.addCommand(createDiffCommand());
|
|
102
103
|
|
|
104
|
+
// AIP-12: Gas-free registration
|
|
105
|
+
program.addCommand(createRegisterCommand());
|
|
106
|
+
|
|
103
107
|
// ============================================================================
|
|
104
108
|
// Error Handling
|
|
105
109
|
// ============================================================================
|
package/src/cli/utils/config.ts
CHANGED
|
@@ -35,6 +35,15 @@ export interface CLIConfig {
|
|
|
35
35
|
/** Optional: RPC URL override */
|
|
36
36
|
rpcUrl?: string;
|
|
37
37
|
|
|
38
|
+
/** AIP-12: Wallet type — 'auto' (Smart Wallet, gasless) or 'eoa' (traditional) */
|
|
39
|
+
wallet?: 'auto' | 'eoa';
|
|
40
|
+
|
|
41
|
+
/** AIP-12: Smart Wallet address (set when wallet=auto, used by `actp register`) */
|
|
42
|
+
smartWallet?: string;
|
|
43
|
+
|
|
44
|
+
/** AIP-12: Whether agent is registered on AgentRegistry */
|
|
45
|
+
registered?: boolean;
|
|
46
|
+
|
|
38
47
|
/** Configuration version for migrations */
|
|
39
48
|
version: string;
|
|
40
49
|
}
|