@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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull Command - Pull on-chain config to local AGIRAILS.md
|
|
3
|
+
*
|
|
4
|
+
* Fetches the published AGIRAILS.md from IPFS (via on-chain CID),
|
|
5
|
+
* verifies integrity against on-chain hash, and writes locally.
|
|
6
|
+
* Use --force to overwrite existing file without confirmation.
|
|
7
|
+
*
|
|
8
|
+
* @module cli/commands/pull
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import { Output, ExitCode } from '../utils/output';
|
|
13
|
+
import { mapError } from '../utils/client';
|
|
14
|
+
import { resolve } from 'path';
|
|
15
|
+
import { ethers } from 'ethers';
|
|
16
|
+
import { pull } from '../../config/syncOperations';
|
|
17
|
+
import { getNetwork } from '../../config/networks';
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Command Definition
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
export function createPullCommand(): Command {
|
|
24
|
+
const cmd = new Command('pull')
|
|
25
|
+
.description('Pull on-chain AGIRAILS.md config to local file')
|
|
26
|
+
.argument('[path]', 'Path to write AGIRAILS.md', './AGIRAILS.md')
|
|
27
|
+
.option('-n, --network <network>', 'Network (base-sepolia | base-mainnet)', 'base-sepolia')
|
|
28
|
+
.option('-a, --address <address>', 'Agent address to pull config for')
|
|
29
|
+
.option('--force', 'Overwrite without confirmation (CI mode)')
|
|
30
|
+
.option('--json', 'Output as JSON')
|
|
31
|
+
.option('-q, --quiet', 'Minimal output')
|
|
32
|
+
.action(async (path, options) => {
|
|
33
|
+
const output = new Output(
|
|
34
|
+
options.json ? 'json' : options.quiet ? 'quiet' : 'human'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await runPull(path, options, output);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const structuredError = mapError(error);
|
|
41
|
+
output.errorResult({
|
|
42
|
+
code: structuredError.code,
|
|
43
|
+
message: structuredError.message,
|
|
44
|
+
details: structuredError.details,
|
|
45
|
+
});
|
|
46
|
+
process.exit(ExitCode.ERROR);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return cmd;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Implementation
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
interface PullCommandOptions {
|
|
58
|
+
network: string;
|
|
59
|
+
address?: string;
|
|
60
|
+
force?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function runPull(
|
|
64
|
+
filePath: string,
|
|
65
|
+
options: PullCommandOptions,
|
|
66
|
+
output: Output
|
|
67
|
+
): Promise<void> {
|
|
68
|
+
const resolvedPath = resolve(filePath);
|
|
69
|
+
|
|
70
|
+
// Determine agent address
|
|
71
|
+
let agentAddress = options.address;
|
|
72
|
+
if (!agentAddress) {
|
|
73
|
+
const privateKey = process.env.ACTP_PRIVATE_KEY || process.env.PRIVATE_KEY;
|
|
74
|
+
if (!privateKey) {
|
|
75
|
+
output.error('Agent address required. Use --address or set ACTP_PRIVATE_KEY env var.');
|
|
76
|
+
process.exit(ExitCode.INVALID_INPUT);
|
|
77
|
+
}
|
|
78
|
+
agentAddress = new ethers.Wallet(privateKey).address;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const networkConfig = getNetwork(options.network);
|
|
82
|
+
if (!networkConfig.contracts.agentRegistry) {
|
|
83
|
+
output.error(`AgentRegistry not deployed on ${options.network}`);
|
|
84
|
+
process.exit(ExitCode.ERROR);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const spinner = output.spinner('Pulling config from on-chain...');
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const provider = new ethers.JsonRpcProvider(networkConfig.rpcUrl);
|
|
91
|
+
|
|
92
|
+
const result = await pull({
|
|
93
|
+
path: resolvedPath,
|
|
94
|
+
agentAddress,
|
|
95
|
+
registryAddress: networkConfig.contracts.agentRegistry,
|
|
96
|
+
provider,
|
|
97
|
+
force: options.force,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
spinner.stop(result.written);
|
|
101
|
+
|
|
102
|
+
output.result(
|
|
103
|
+
{
|
|
104
|
+
written: result.written,
|
|
105
|
+
cid: result.cid || null,
|
|
106
|
+
status: result.status,
|
|
107
|
+
path: resolvedPath,
|
|
108
|
+
network: options.network,
|
|
109
|
+
},
|
|
110
|
+
{ quietKey: 'status' }
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (result.written) {
|
|
114
|
+
output.blank();
|
|
115
|
+
output.success(`Config pulled and written to ${filePath}`);
|
|
116
|
+
} else {
|
|
117
|
+
output.blank();
|
|
118
|
+
output.info(result.status);
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
spinner.stop(false);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -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
|
@@ -48,6 +48,10 @@ import { createWatchCommand } from './commands/watch';
|
|
|
48
48
|
import { createSimulateCommand } from './commands/simulate';
|
|
49
49
|
import { createBatchCommand } from './commands/batch';
|
|
50
50
|
import { createTimeCommand } from './commands/time';
|
|
51
|
+
import { createPublishCommand } from './commands/publish';
|
|
52
|
+
import { createPullCommand } from './commands/pull';
|
|
53
|
+
import { createDiffCommand } from './commands/diff';
|
|
54
|
+
import { createRegisterCommand } from './commands/register';
|
|
51
55
|
|
|
52
56
|
// ============================================================================
|
|
53
57
|
// Program Setup
|
|
@@ -92,6 +96,14 @@ program.addCommand(createBatchCommand());
|
|
|
92
96
|
// Mock mode utilities
|
|
93
97
|
program.addCommand(createTimeCommand());
|
|
94
98
|
|
|
99
|
+
// Config sync commands (AGIRAILS.md as source of truth)
|
|
100
|
+
program.addCommand(createPublishCommand());
|
|
101
|
+
program.addCommand(createPullCommand());
|
|
102
|
+
program.addCommand(createDiffCommand());
|
|
103
|
+
|
|
104
|
+
// AIP-12: Gas-free registration
|
|
105
|
+
program.addCommand(createRegisterCommand());
|
|
106
|
+
|
|
95
107
|
// ============================================================================
|
|
96
108
|
// Error Handling
|
|
97
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
|
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AGIRAILS.md Parser + Canonical Hash
|
|
3
|
+
*
|
|
4
|
+
* Parses AGIRAILS.md files (YAML frontmatter + markdown body),
|
|
5
|
+
* computes deterministic canonical hashes for on-chain verification.
|
|
6
|
+
*
|
|
7
|
+
* ## Canonical Hash Algorithm
|
|
8
|
+
*
|
|
9
|
+
* 1. Parse YAML frontmatter into a plain object
|
|
10
|
+
* 2. **Strip publish metadata keys** (config_hash, published_at, config_cid, arweave_tx)
|
|
11
|
+
* — these are written back by the publish pipeline and must not affect the hash
|
|
12
|
+
* 3. Canonicalize frontmatter:
|
|
13
|
+
* - Object keys: sorted lexicographically (recursive)
|
|
14
|
+
* - Primitive arrays: sorted lexicographically by `String(value).localeCompare()`
|
|
15
|
+
* - Object arrays: order preserved (semantic ordering matters)
|
|
16
|
+
* - Date objects: converted to ISO-8601 string (`.toISOString()`)
|
|
17
|
+
* - null/undefined: preserved as-is
|
|
18
|
+
* 4. `structuredHash = keccak256(JSON.stringify(canonical))`
|
|
19
|
+
* 5. Normalize body: CRLF→LF, strip trailing whitespace per line, trim
|
|
20
|
+
* 6. `bodyHash = keccak256(normalizedBody)`
|
|
21
|
+
* 7. `configHash = keccak256(structuredHash ++ bodyHash)` (byte concatenation)
|
|
22
|
+
*
|
|
23
|
+
* @module config/agirailsmd
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { ethers } from 'ethers';
|
|
27
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Types
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
export interface AgirailsMdConfig {
|
|
34
|
+
/** Parsed YAML frontmatter as a plain object */
|
|
35
|
+
frontmatter: Record<string, unknown>;
|
|
36
|
+
/** Markdown body (everything after the closing ---) */
|
|
37
|
+
body: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AgirailsMdHashResult {
|
|
41
|
+
/** Final configHash = keccak256(structuredHash + bodyHash) */
|
|
42
|
+
configHash: string;
|
|
43
|
+
/** Hash of the canonical JSON representation of frontmatter */
|
|
44
|
+
structuredHash: string;
|
|
45
|
+
/** Hash of the normalized markdown body */
|
|
46
|
+
bodyHash: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Publish Metadata (excluded from hash computation)
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Frontmatter keys written by the publish pipeline.
|
|
55
|
+
* These are stripped before hash computation to prevent self-reference drift:
|
|
56
|
+
* publish writes config_hash back → changes frontmatter → changes hash → never in sync.
|
|
57
|
+
*/
|
|
58
|
+
export const PUBLISH_METADATA_KEYS = [
|
|
59
|
+
'config_hash',
|
|
60
|
+
'published_at',
|
|
61
|
+
'config_cid',
|
|
62
|
+
'arweave_tx',
|
|
63
|
+
'template_source',
|
|
64
|
+
] as const;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Strip publish metadata keys from a frontmatter object.
|
|
68
|
+
* Returns a shallow copy with the metadata keys removed.
|
|
69
|
+
*/
|
|
70
|
+
export function stripPublishMetadata(
|
|
71
|
+
frontmatter: Record<string, unknown>
|
|
72
|
+
): Record<string, unknown> {
|
|
73
|
+
const stripped = { ...frontmatter };
|
|
74
|
+
for (const key of PUBLISH_METADATA_KEYS) {
|
|
75
|
+
delete stripped[key];
|
|
76
|
+
}
|
|
77
|
+
return stripped;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// Parser
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Parse an AGIRAILS.md file into frontmatter + body.
|
|
86
|
+
*
|
|
87
|
+
* @param content - Raw file content (string)
|
|
88
|
+
* @returns Parsed config with frontmatter object and body string
|
|
89
|
+
* @throws Error if content has no valid YAML frontmatter
|
|
90
|
+
*/
|
|
91
|
+
export function parseAgirailsMd(content: string): AgirailsMdConfig {
|
|
92
|
+
const trimmed = content.trimStart();
|
|
93
|
+
|
|
94
|
+
if (!trimmed.startsWith('---')) {
|
|
95
|
+
throw new Error('AGIRAILS.md must start with YAML frontmatter (---)');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Find closing ---
|
|
99
|
+
const closingIndex = trimmed.indexOf('\n---', 3);
|
|
100
|
+
if (closingIndex === -1) {
|
|
101
|
+
throw new Error('AGIRAILS.md frontmatter is not closed (missing closing ---)');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const yamlContent = trimmed.slice(4, closingIndex); // skip opening ---\n
|
|
105
|
+
const body = trimmed.slice(closingIndex + 4); // skip \n---
|
|
106
|
+
|
|
107
|
+
// Parse YAML
|
|
108
|
+
let frontmatter: Record<string, unknown>;
|
|
109
|
+
try {
|
|
110
|
+
frontmatter = parseYaml(yamlContent);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
113
|
+
throw new Error(`Failed to parse YAML frontmatter: ${message}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (typeof frontmatter !== 'object' || frontmatter === null) {
|
|
117
|
+
throw new Error('YAML frontmatter must be an object');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
frontmatter,
|
|
122
|
+
body: body.startsWith('\n') ? body.slice(1) : body,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ============================================================================
|
|
127
|
+
// Canonical Hash
|
|
128
|
+
// ============================================================================
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Recursively canonicalize a value for deterministic JSON serialization.
|
|
132
|
+
*
|
|
133
|
+
* - Object keys: sorted lexicographically
|
|
134
|
+
* - Primitive arrays: sorted by String(x).localeCompare()
|
|
135
|
+
* - Object arrays: order preserved
|
|
136
|
+
* - Date objects: converted to ISO-8601 string
|
|
137
|
+
* - null/undefined: preserved
|
|
138
|
+
*/
|
|
139
|
+
export function canonicalize(value: unknown): unknown {
|
|
140
|
+
if (value === null || value === undefined) {
|
|
141
|
+
return value;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Handle Date objects deterministically (YAML parser may auto-create these)
|
|
145
|
+
if (value instanceof Date) {
|
|
146
|
+
return value.toISOString();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (Array.isArray(value)) {
|
|
150
|
+
// Canonicalize each element, then sort arrays of primitives lexicographically
|
|
151
|
+
const canonicalized = value.map(canonicalize);
|
|
152
|
+
|
|
153
|
+
// Only sort arrays of primitives (strings, numbers, booleans)
|
|
154
|
+
// Arrays of objects maintain order (e.g., onboarding questions have semantic ordering)
|
|
155
|
+
const allPrimitive = canonicalized.every(
|
|
156
|
+
(item) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean'
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (allPrimitive) {
|
|
160
|
+
return canonicalized.sort((a, b) => String(a).localeCompare(String(b)));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return canonicalized;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof value === 'object') {
|
|
167
|
+
const sorted: Record<string, unknown> = {};
|
|
168
|
+
const keys = Object.keys(value as Record<string, unknown>).sort();
|
|
169
|
+
for (const key of keys) {
|
|
170
|
+
sorted[key] = canonicalize((value as Record<string, unknown>)[key]);
|
|
171
|
+
}
|
|
172
|
+
return sorted;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Normalize markdown body for deterministic hashing.
|
|
180
|
+
* - Strip trailing whitespace from each line
|
|
181
|
+
* - Ensure \n line endings
|
|
182
|
+
* - Trim leading/trailing whitespace
|
|
183
|
+
*/
|
|
184
|
+
function normalizeBody(body: string): string {
|
|
185
|
+
return body
|
|
186
|
+
.replace(/\r\n/g, '\n') // CRLF → LF
|
|
187
|
+
.replace(/\r/g, '\n') // CR → LF
|
|
188
|
+
.split('\n')
|
|
189
|
+
.map((line) => line.trimEnd()) // strip trailing whitespace per line
|
|
190
|
+
.join('\n')
|
|
191
|
+
.trim(); // trim leading/trailing
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Compute the canonical config hash from raw AGIRAILS.md content.
|
|
196
|
+
*
|
|
197
|
+
* @param content - Raw AGIRAILS.md file content
|
|
198
|
+
* @returns Hash result with configHash, structuredHash, and bodyHash
|
|
199
|
+
*/
|
|
200
|
+
export function computeConfigHash(content: string): AgirailsMdHashResult {
|
|
201
|
+
const { frontmatter, body } = parseAgirailsMd(content);
|
|
202
|
+
return computeConfigHashFromParts(frontmatter, body);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Compute the canonical config hash from parsed parts.
|
|
207
|
+
*
|
|
208
|
+
* Publish metadata keys (config_hash, published_at, config_cid, arweave_tx)
|
|
209
|
+
* are automatically stripped before hashing to prevent self-reference drift.
|
|
210
|
+
*
|
|
211
|
+
* @param frontmatter - Parsed YAML frontmatter object
|
|
212
|
+
* @param body - Markdown body string
|
|
213
|
+
* @returns Hash result with configHash, structuredHash, and bodyHash
|
|
214
|
+
*/
|
|
215
|
+
export function computeConfigHashFromParts(
|
|
216
|
+
frontmatter: Record<string, unknown>,
|
|
217
|
+
body: string
|
|
218
|
+
): AgirailsMdHashResult {
|
|
219
|
+
// Step 0: Strip publish metadata to prevent self-reference drift
|
|
220
|
+
const stripped = stripPublishMetadata(frontmatter);
|
|
221
|
+
|
|
222
|
+
// Step 1: Canonical JSON of frontmatter (with metadata stripped)
|
|
223
|
+
const canonical = canonicalize(stripped);
|
|
224
|
+
const canonicalJson = JSON.stringify(canonical);
|
|
225
|
+
const structuredHash = ethers.keccak256(ethers.toUtf8Bytes(canonicalJson));
|
|
226
|
+
|
|
227
|
+
// Step 2: Normalized body hash
|
|
228
|
+
const normalized = normalizeBody(body);
|
|
229
|
+
const bodyHash = ethers.keccak256(ethers.toUtf8Bytes(normalized));
|
|
230
|
+
|
|
231
|
+
// Step 3: Combined hash
|
|
232
|
+
const configHash = ethers.keccak256(
|
|
233
|
+
ethers.concat([ethers.getBytes(structuredHash), ethers.getBytes(bodyHash)])
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
return { configHash, structuredHash, bodyHash };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ============================================================================
|
|
240
|
+
// Serializer
|
|
241
|
+
// ============================================================================
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Serialize config back to AGIRAILS.md format.
|
|
245
|
+
*
|
|
246
|
+
* @param frontmatter - YAML frontmatter object
|
|
247
|
+
* @param body - Markdown body string
|
|
248
|
+
* @returns Complete AGIRAILS.md file content
|
|
249
|
+
*/
|
|
250
|
+
export function serializeAgirailsMd(
|
|
251
|
+
frontmatter: Record<string, unknown>,
|
|
252
|
+
body: string
|
|
253
|
+
): string {
|
|
254
|
+
const yamlStr = stringifyYaml(frontmatter, {
|
|
255
|
+
lineWidth: 120,
|
|
256
|
+
singleQuote: false,
|
|
257
|
+
}).trimEnd();
|
|
258
|
+
|
|
259
|
+
const normalizedBody = body.startsWith('\n') ? body : `\n${body}`;
|
|
260
|
+
|
|
261
|
+
return `---\n${yamlStr}\n---\n${normalizedBody}`;
|
|
262
|
+
}
|