@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.
- package/README.md +65 -31
- package/dist/ACTPClient.d.ts +42 -1
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +207 -22
- package/dist/ACTPClient.js.map +1 -1
- package/dist/abi/AgentRegistry.json +133 -0
- 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/adapters/X402Adapter.d.ts +34 -7
- package/dist/adapters/X402Adapter.d.ts.map +1 -1
- package/dist/adapters/X402Adapter.js +36 -8
- package/dist/adapters/X402Adapter.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/cli/commands/diff.d.ts +11 -0
- package/dist/cli/commands/diff.d.ts.map +1 -0
- package/dist/cli/commands/diff.js +115 -0
- package/dist/cli/commands/diff.js.map +1 -0
- 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 +260 -19
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/publish.d.ts +11 -0
- package/dist/cli/commands/publish.d.ts.map +1 -0
- package/dist/cli/commands/publish.js +170 -0
- package/dist/cli/commands/publish.js.map +1 -0
- package/dist/cli/commands/pull.d.ts +12 -0
- package/dist/cli/commands/pull.d.ts.map +1 -0
- package/dist/cli/commands/pull.js +99 -0
- package/dist/cli/commands/pull.js.map +1 -0
- 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 +10 -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/agirailsmd.d.ts +94 -0
- package/dist/config/agirailsmd.d.ts.map +1 -0
- package/dist/config/agirailsmd.js +209 -0
- package/dist/config/agirailsmd.js.map +1 -0
- package/dist/config/networks.d.ts +22 -4
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +64 -26
- package/dist/config/networks.js.map +1 -1
- package/dist/config/publishPipeline.d.ts +75 -0
- package/dist/config/publishPipeline.d.ts.map +1 -0
- package/dist/config/publishPipeline.js +193 -0
- package/dist/config/publishPipeline.js.map +1 -0
- package/dist/config/syncOperations.d.ts +67 -0
- package/dist/config/syncOperations.d.ts.map +1 -0
- package/dist/config/syncOperations.js +208 -0
- package/dist/config/syncOperations.js.map +1 -0
- 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 +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/level0/request.d.ts.map +1 -1
- package/dist/level0/request.js +23 -86
- package/dist/level0/request.js.map +1 -1
- package/dist/level1/Agent.d.ts +0 -11
- package/dist/level1/Agent.d.ts.map +1 -1
- package/dist/level1/Agent.js +19 -36
- 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/registry/AgentRegistryClient.d.ts +75 -0
- package/dist/registry/AgentRegistryClient.d.ts.map +1 -0
- package/dist/registry/AgentRegistryClient.js +160 -0
- package/dist/registry/AgentRegistryClient.js.map +1 -0
- 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/types/adapter.d.ts +39 -0
- package/dist/types/adapter.d.ts.map +1 -1
- package/dist/types/adapter.js +7 -0
- package/dist/types/adapter.js.map +1 -1
- package/dist/types/x402.d.ts +23 -0
- package/dist/types/x402.d.ts.map +1 -1
- package/dist/types/x402.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/dist/wallet/keystore.d.ts +16 -0
- package/dist/wallet/keystore.d.ts.map +1 -0
- package/dist/wallet/keystore.js +132 -0
- package/dist/wallet/keystore.js.map +1 -0
- package/package.json +5 -2
- package/src/ACTPClient.ts +275 -27
- package/src/abi/AgentRegistry.json +133 -0
- package/src/adapters/AdapterRouter.ts +0 -1
- package/src/adapters/BasicAdapter.ts +41 -1
- package/src/adapters/X402Adapter.ts +94 -16
- package/src/adapters/index.ts +9 -1
- package/src/cli/commands/diff.ts +141 -0
- package/src/cli/commands/init.ts +311 -22
- package/src/cli/commands/publish.ts +208 -0
- package/src/cli/commands/pull.ts +124 -0
- package/src/cli/commands/register.ts +233 -0
- package/src/cli/index.ts +12 -0
- package/src/cli/utils/config.ts +9 -0
- package/src/config/agirailsmd.ts +262 -0
- package/src/config/networks.ts +89 -26
- package/src/config/publishPipeline.ts +276 -0
- package/src/config/syncOperations.ts +279 -0
- package/src/erc8004/ERC8004Bridge.ts +6 -5
- package/src/erc8004/ReputationReporter.ts +14 -18
- package/src/index.ts +15 -0
- package/src/level0/request.ts +27 -88
- package/src/level1/Agent.ts +21 -37
- package/src/protocol/ACTPKernel.ts +20 -10
- package/src/protocol/EventMonitor.ts +14 -0
- package/src/registry/AgentRegistryClient.ts +202 -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/types/adapter.ts +14 -0
- package/src/types/x402.ts +32 -0
- 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/wallet/keystore.ts +119 -0
package/src/cli/commands/init.ts
CHANGED
|
@@ -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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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: '
|
|
240
|
-
moduleResolution: '
|
|
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
|
-
//
|
|
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
|
+
}
|