@agirails/sdk 2.5.3 → 2.5.5
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/dist/ACTPClient.d.ts +18 -0
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +72 -23
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +15 -0
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +33 -4
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/adapters/StandardAdapter.d.ts +20 -3
- package/dist/adapters/StandardAdapter.d.ts.map +1 -1
- package/dist/adapters/StandardAdapter.js +90 -12
- package/dist/adapters/StandardAdapter.js.map +1 -1
- package/dist/cli/commands/publish.js +16 -4
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.js +16 -4
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/tx.js +31 -3
- package/dist/cli/commands/tx.js.map +1 -1
- package/dist/config/networks.d.ts +10 -2
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +31 -22
- package/dist/config/networks.js.map +1 -1
- package/dist/level0/request.d.ts.map +1 -1
- package/dist/level0/request.js +2 -1
- package/dist/level0/request.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +11 -5
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -1
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +27 -7
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +11 -1
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
- package/dist/wallet/AutoWalletProvider.js +84 -19
- package/dist/wallet/AutoWalletProvider.js.map +1 -1
- package/dist/wallet/IWalletProvider.d.ts +34 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -1
- package/dist/wallet/SmartWalletRouter.d.ts +128 -0
- package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
- package/dist/wallet/SmartWalletRouter.js +248 -0
- package/dist/wallet/SmartWalletRouter.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +26 -1
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
- package/dist/wallet/aa/DualNonceManager.js +140 -6
- package/dist/wallet/aa/DualNonceManager.js.map +1 -1
- package/package.json +3 -6
- package/src/ACTPClient.ts +0 -1579
- package/src/abi/ACTPKernel.json +0 -1356
- package/src/abi/AgentRegistry.json +0 -915
- package/src/abi/ERC20.json +0 -40
- package/src/abi/EscrowVault.json +0 -134
- package/src/abi/IdentityRegistry.json +0 -316
- package/src/adapters/AdapterRegistry.ts +0 -173
- package/src/adapters/AdapterRouter.ts +0 -416
- package/src/adapters/BaseAdapter.ts +0 -498
- package/src/adapters/BasicAdapter.ts +0 -514
- package/src/adapters/IAdapter.ts +0 -292
- package/src/adapters/StandardAdapter.ts +0 -555
- package/src/adapters/X402Adapter.ts +0 -731
- package/src/adapters/index.ts +0 -60
- package/src/builders/DeliveryProofBuilder.ts +0 -327
- package/src/builders/QuoteBuilder.ts +0 -483
- package/src/builders/index.ts +0 -17
- package/src/cli/commands/balance.ts +0 -110
- package/src/cli/commands/batch.ts +0 -487
- package/src/cli/commands/config.ts +0 -231
- package/src/cli/commands/deploy-check.ts +0 -364
- package/src/cli/commands/deploy-env.ts +0 -120
- package/src/cli/commands/diff.ts +0 -141
- package/src/cli/commands/init.ts +0 -469
- package/src/cli/commands/mint.ts +0 -116
- package/src/cli/commands/pay.ts +0 -113
- package/src/cli/commands/publish.ts +0 -475
- package/src/cli/commands/pull.ts +0 -124
- package/src/cli/commands/register.ts +0 -247
- package/src/cli/commands/simulate.ts +0 -345
- package/src/cli/commands/time.ts +0 -302
- package/src/cli/commands/tx.ts +0 -448
- package/src/cli/commands/watch.ts +0 -211
- package/src/cli/index.ts +0 -134
- package/src/cli/utils/client.ts +0 -252
- package/src/cli/utils/config.ts +0 -389
- package/src/cli/utils/output.ts +0 -465
- package/src/cli/utils/wallet.ts +0 -109
- package/src/config/agirailsmd.ts +0 -262
- package/src/config/networks.ts +0 -275
- package/src/config/pendingPublish.ts +0 -237
- package/src/config/publishPipeline.ts +0 -359
- package/src/config/syncOperations.ts +0 -279
- package/src/erc8004/ERC8004Bridge.ts +0 -462
- package/src/erc8004/ReputationReporter.ts +0 -468
- package/src/erc8004/index.ts +0 -61
- package/src/errors/index.ts +0 -427
- package/src/index.ts +0 -364
- package/src/level0/Provider.ts +0 -117
- package/src/level0/ServiceDirectory.ts +0 -131
- package/src/level0/index.ts +0 -10
- package/src/level0/provide.ts +0 -132
- package/src/level0/request.ts +0 -432
- package/src/level1/Agent.ts +0 -1426
- package/src/level1/index.ts +0 -10
- package/src/level1/pricing/PriceCalculator.ts +0 -255
- package/src/level1/pricing/PricingStrategy.ts +0 -198
- package/src/level1/types/Job.ts +0 -179
- package/src/level1/types/Options.ts +0 -291
- package/src/level1/types/index.ts +0 -8
- package/src/protocol/ACTPKernel.ts +0 -808
- package/src/protocol/AgentRegistry.ts +0 -559
- package/src/protocol/DIDManager.ts +0 -629
- package/src/protocol/DIDResolver.ts +0 -554
- package/src/protocol/EASHelper.ts +0 -378
- package/src/protocol/EscrowVault.ts +0 -255
- package/src/protocol/EventMonitor.ts +0 -204
- package/src/protocol/MessageSigner.ts +0 -510
- package/src/protocol/ProofGenerator.ts +0 -339
- package/src/protocol/QuoteBuilder.ts +0 -15
- package/src/registry/AgentRegistryClient.ts +0 -202
- package/src/runtime/BlockchainRuntime.ts +0 -1015
- package/src/runtime/IACTPRuntime.ts +0 -306
- package/src/runtime/MockRuntime.ts +0 -1298
- package/src/runtime/MockStateManager.ts +0 -577
- package/src/runtime/index.ts +0 -25
- package/src/runtime/types/MockState.ts +0 -237
- package/src/storage/ArchiveBundleBuilder.ts +0 -561
- package/src/storage/ArweaveClient.ts +0 -946
- package/src/storage/FilebaseClient.ts +0 -790
- package/src/storage/index.ts +0 -96
- package/src/storage/types.ts +0 -348
- package/src/types/adapter.ts +0 -310
- package/src/types/agent.ts +0 -79
- package/src/types/did.ts +0 -223
- package/src/types/eip712.ts +0 -175
- package/src/types/erc8004.ts +0 -293
- package/src/types/escrow.ts +0 -27
- package/src/types/index.ts +0 -17
- package/src/types/message.ts +0 -145
- package/src/types/state.ts +0 -87
- package/src/types/transaction.ts +0 -69
- package/src/types/x402.ts +0 -251
- package/src/utils/ErrorRecoveryGuide.ts +0 -676
- package/src/utils/Helpers.ts +0 -688
- package/src/utils/IPFSClient.ts +0 -368
- package/src/utils/Logger.ts +0 -484
- package/src/utils/NonceManager.ts +0 -591
- package/src/utils/RateLimiter.ts +0 -534
- package/src/utils/ReceivedNonceTracker.ts +0 -567
- package/src/utils/SDKLifecycle.ts +0 -416
- package/src/utils/SecureNonce.ts +0 -78
- package/src/utils/Semaphore.ts +0 -276
- package/src/utils/UsedAttestationTracker.ts +0 -385
- package/src/utils/canonicalJson.ts +0 -38
- package/src/utils/circuitBreaker.ts +0 -324
- package/src/utils/computeTypeHash.ts +0 -48
- package/src/utils/fsSafe.ts +0 -80
- package/src/utils/index.ts +0 -80
- package/src/utils/retry.ts +0 -364
- package/src/utils/security.ts +0 -418
- package/src/utils/validation.ts +0 -540
- package/src/wallet/AutoWalletProvider.ts +0 -299
- package/src/wallet/EOAWalletProvider.ts +0 -69
- package/src/wallet/IWalletProvider.ts +0 -135
- package/src/wallet/aa/BundlerClient.ts +0 -274
- package/src/wallet/aa/DualNonceManager.ts +0 -173
- package/src/wallet/aa/PaymasterClient.ts +0 -174
- package/src/wallet/aa/TransactionBatcher.ts +0 -353
- package/src/wallet/aa/UserOpBuilder.ts +0 -246
- package/src/wallet/aa/constants.ts +0 -60
- package/src/wallet/keystore.ts +0 -240
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Publish Command — Lazy Publish flow.
|
|
3
|
-
*
|
|
4
|
-
* New flow (no on-chain calls):
|
|
5
|
-
* 1. Parse AGIRAILS.md → compute configHash
|
|
6
|
-
* 2. Generate wallet if .actp/keystore.json missing
|
|
7
|
-
* 3. Upload to IPFS via Filebase
|
|
8
|
-
* 4. Optionally upload to Arweave
|
|
9
|
-
* 5. Save pending-publish.json (activation deferred to first payment)
|
|
10
|
-
* 6. Update AGIRAILS.md frontmatter
|
|
11
|
-
*
|
|
12
|
-
* On-chain activation happens automatically during the first payment
|
|
13
|
-
* via ACTPClient's lazy publish mechanism.
|
|
14
|
-
*
|
|
15
|
-
* @module cli/commands/publish
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { Command } from 'commander';
|
|
19
|
-
import { Output, ExitCode } from '../utils/output';
|
|
20
|
-
import { mapError } from '../utils/client';
|
|
21
|
-
import { resolve, join } from 'path';
|
|
22
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
23
|
-
import { computeConfigHash, serializeAgirailsMd, parseAgirailsMd } from '../../config/agirailsmd';
|
|
24
|
-
import { preparePublish, extractRegistrationParams, PENDING_ENDPOINT } from '../../config/publishPipeline';
|
|
25
|
-
import { savePendingPublish, getActpDir } from '../../config/pendingPublish';
|
|
26
|
-
import { addToGitignore, loadConfig, saveConfig, isInitialized, CLIConfig, CONFIG_DEFAULTS } from '../utils/config';
|
|
27
|
-
import { FilebaseClient } from '../../storage/FilebaseClient';
|
|
28
|
-
import { ArweaveClient } from '../../storage/ArweaveClient';
|
|
29
|
-
import { generateWallet } from '../utils/wallet';
|
|
30
|
-
|
|
31
|
-
// ============================================================================
|
|
32
|
-
// Publish Proxy (fallback when no Filebase credentials)
|
|
33
|
-
// ============================================================================
|
|
34
|
-
|
|
35
|
-
const PUBLISH_PROXY_URL = process.env.AGIRAILS_PUBLISH_URL || 'https://api.agirails.io/v1/publish';
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Public client key for the AGIRAILS publish proxy.
|
|
39
|
-
* This is NOT a secret — it's a rate-limited, revocable identifier
|
|
40
|
-
* (same model as Firebase API keys). Override via AGIRAILS_PUBLISH_KEY.
|
|
41
|
-
*/
|
|
42
|
-
const PUBLISH_CLIENT_KEY = process.env.AGIRAILS_PUBLISH_KEY || 'ag_pub_v1_2026';
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Upload AGIRAILS.md content via the AGIRAILS publish proxy API.
|
|
46
|
-
* Used when FILEBASE_ACCESS_KEY / FILEBASE_SECRET_KEY are not set.
|
|
47
|
-
*
|
|
48
|
-
* @param content - Raw AGIRAILS.md file content
|
|
49
|
-
* @param localConfigHash - Locally computed configHash (for validation)
|
|
50
|
-
*/
|
|
51
|
-
async function publishViaProxy(content: string, localConfigHash: string): Promise<{ cid: string; configHash: string }> {
|
|
52
|
-
const response = await fetch(PUBLISH_PROXY_URL, {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers: {
|
|
55
|
-
'Content-Type': 'application/json',
|
|
56
|
-
'X-API-Key': PUBLISH_CLIENT_KEY,
|
|
57
|
-
},
|
|
58
|
-
body: JSON.stringify({ content }),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
if (!response.ok) {
|
|
62
|
-
const body = await response.json().catch(() => ({ error: response.statusText })) as { error?: string };
|
|
63
|
-
throw new Error(`Publish proxy error (${response.status}): ${body.error || response.statusText}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const result = await response.json() as { cid: string; configHash: string };
|
|
67
|
-
if (!result.cid || !result.configHash) {
|
|
68
|
-
throw new Error('Publish proxy returned invalid response (missing cid or configHash)');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Validate: proxy's configHash must match locally computed hash
|
|
72
|
-
// (guards against hash-drift between SDK and API canonicalization logic)
|
|
73
|
-
if (result.configHash !== localConfigHash) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`Config hash mismatch: proxy returned ${result.configHash.slice(0, 10)}... ` +
|
|
76
|
-
`but local hash is ${localConfigHash.slice(0, 10)}... — ` +
|
|
77
|
-
'This may indicate an SDK/API version mismatch. Update your SDK.'
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ============================================================================
|
|
85
|
-
// Command Definition
|
|
86
|
-
// ============================================================================
|
|
87
|
-
|
|
88
|
-
export function createPublishCommand(): Command {
|
|
89
|
-
const cmd = new Command('publish')
|
|
90
|
-
.description('Publish AGIRAILS.md config (offline — activates on first payment)')
|
|
91
|
-
.argument('[path]', 'Path to AGIRAILS.md', './AGIRAILS.md')
|
|
92
|
-
.option('-n, --network <network>', 'DEPRECATED: network is auto-detected (accepted but ignored)')
|
|
93
|
-
.option('--skip-arweave', 'Skip permanent Arweave storage (dev mode)')
|
|
94
|
-
.option('--dry-run', 'Show what would happen without executing')
|
|
95
|
-
.option('--json', 'Output as JSON')
|
|
96
|
-
.option('-q, --quiet', 'Output only the config hash')
|
|
97
|
-
.action(async (path, options) => {
|
|
98
|
-
const output = new Output(
|
|
99
|
-
options.json ? 'json' : options.quiet ? 'quiet' : 'human'
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
await runPublish(path, options, output);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
const structuredError = mapError(error);
|
|
106
|
-
output.errorResult({
|
|
107
|
-
code: structuredError.code,
|
|
108
|
-
message: structuredError.message,
|
|
109
|
-
details: structuredError.details,
|
|
110
|
-
});
|
|
111
|
-
process.exit(ExitCode.ERROR);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
return cmd;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ============================================================================
|
|
119
|
-
// Implementation
|
|
120
|
-
// ============================================================================
|
|
121
|
-
|
|
122
|
-
interface PublishCommandOptions {
|
|
123
|
-
network?: string;
|
|
124
|
-
skipArweave?: boolean;
|
|
125
|
-
dryRun?: boolean;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function runPublish(
|
|
129
|
-
filePath: string,
|
|
130
|
-
options: PublishCommandOptions,
|
|
131
|
-
output: Output
|
|
132
|
-
): Promise<void> {
|
|
133
|
-
// Deprecation warning for --network
|
|
134
|
-
if (options.network) {
|
|
135
|
-
output.warning('--network flag is deprecated and ignored. Network is auto-detected at payment time.');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const resolvedPath = resolve(filePath);
|
|
139
|
-
|
|
140
|
-
if (!existsSync(resolvedPath)) {
|
|
141
|
-
output.error(`File not found: ${filePath}`);
|
|
142
|
-
process.exit(ExitCode.INVALID_INPUT);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const spinner = output.spinner('Reading AGIRAILS.md...');
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
// Read and compute hash
|
|
149
|
-
const content = readFileSync(resolvedPath, 'utf-8');
|
|
150
|
-
const { configHash, structuredHash, bodyHash } = computeConfigHash(content);
|
|
151
|
-
|
|
152
|
-
if (options.dryRun) {
|
|
153
|
-
spinner.stop(true);
|
|
154
|
-
|
|
155
|
-
output.result(
|
|
156
|
-
{
|
|
157
|
-
configHash,
|
|
158
|
-
structuredHash,
|
|
159
|
-
bodyHash,
|
|
160
|
-
path: resolvedPath,
|
|
161
|
-
dryRun: true,
|
|
162
|
-
},
|
|
163
|
-
{ quietKey: 'configHash' }
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
output.blank();
|
|
167
|
-
output.success('Dry run complete. No changes made.');
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Ensure .actp directory exists
|
|
172
|
-
const actpDir = getActpDir();
|
|
173
|
-
if (!existsSync(actpDir)) {
|
|
174
|
-
mkdirSync(actpDir, { recursive: true });
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Generate wallet if keystore.json doesn't exist
|
|
178
|
-
const keystorePath = join(actpDir, 'keystore.json');
|
|
179
|
-
let walletAddress = '';
|
|
180
|
-
if (!existsSync(keystorePath)) {
|
|
181
|
-
spinner.stop(true);
|
|
182
|
-
output.info('No wallet found — generating one...');
|
|
183
|
-
walletAddress = await generateWallet(actpDir, output);
|
|
184
|
-
output.blank();
|
|
185
|
-
} else {
|
|
186
|
-
// Read address from existing keystore (plaintext field, no decryption)
|
|
187
|
-
try {
|
|
188
|
-
const ks = JSON.parse(readFileSync(keystorePath, 'utf-8'));
|
|
189
|
-
walletAddress = ks.address ? `0x${ks.address.replace(/^0x/, '')}` : '';
|
|
190
|
-
} catch {
|
|
191
|
-
// Best-effort address extraction
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Upload to IPFS: direct (Filebase) or via publish proxy
|
|
196
|
-
const filebaseAccessKey = process.env.FILEBASE_ACCESS_KEY;
|
|
197
|
-
const filebaseSecretKey = process.env.FILEBASE_SECRET_KEY;
|
|
198
|
-
const useProxy = !filebaseAccessKey || !filebaseSecretKey;
|
|
199
|
-
|
|
200
|
-
spinner.stop(true);
|
|
201
|
-
|
|
202
|
-
if (useProxy) {
|
|
203
|
-
output.info('No Filebase credentials — using AGIRAILS publish proxy.');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const publishSpinner = output.spinner('Publishing to IPFS...');
|
|
207
|
-
|
|
208
|
-
let cid: string;
|
|
209
|
-
let arweaveTxId: string | undefined;
|
|
210
|
-
|
|
211
|
-
if (useProxy) {
|
|
212
|
-
// Fallback: upload via AGIRAILS publish proxy API
|
|
213
|
-
const proxyResult = await publishViaProxy(content, configHash);
|
|
214
|
-
cid = proxyResult.cid;
|
|
215
|
-
// Proxy doesn't do Arweave — skip
|
|
216
|
-
} else {
|
|
217
|
-
// Direct upload via Filebase S3 + optional Arweave
|
|
218
|
-
const filebaseClient = new FilebaseClient({
|
|
219
|
-
accessKey: filebaseAccessKey,
|
|
220
|
-
secretKey: filebaseSecretKey,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
let arweaveClient: ArweaveClient | undefined;
|
|
224
|
-
if (!options.skipArweave) {
|
|
225
|
-
const arweaveKey = process.env.ARCHIVE_UPLOADER_KEY;
|
|
226
|
-
if (arweaveKey) {
|
|
227
|
-
arweaveClient = await ArweaveClient.create({
|
|
228
|
-
privateKey: arweaveKey,
|
|
229
|
-
rpcUrl: 'https://mainnet.base.org',
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const prepResult = await preparePublish({
|
|
235
|
-
path: resolvedPath,
|
|
236
|
-
filebaseClient,
|
|
237
|
-
arweaveClient,
|
|
238
|
-
skipArweave: options.skipArweave || !arweaveClient,
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
cid = prepResult.cid;
|
|
242
|
-
arweaveTxId = prepResult.arweaveTxId;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
publishSpinner.stop(true);
|
|
246
|
-
|
|
247
|
-
// Parse frontmatter for registration params
|
|
248
|
-
const { frontmatter, body } = parseAgirailsMd(content);
|
|
249
|
-
const regParams = extractRegistrationParams(frontmatter as Record<string, unknown>);
|
|
250
|
-
|
|
251
|
-
const pendingData = {
|
|
252
|
-
version: 1 as const,
|
|
253
|
-
configHash,
|
|
254
|
-
cid,
|
|
255
|
-
endpoint: regParams.endpoint,
|
|
256
|
-
serviceDescriptors: regParams.serviceDescriptors,
|
|
257
|
-
createdAt: new Date().toISOString(),
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const projectRoot = resolve(filePath, '..');
|
|
261
|
-
|
|
262
|
-
// ================================================================
|
|
263
|
-
// Local setup: bootstrap or migrate config, ensure .gitignore
|
|
264
|
-
// "publish covers setup" — no separate `actp init` required.
|
|
265
|
-
// ================================================================
|
|
266
|
-
try {
|
|
267
|
-
if (isInitialized(projectRoot)) {
|
|
268
|
-
// Existing project: migrate config (strip deprecated `registered`)
|
|
269
|
-
const config = loadConfig(projectRoot);
|
|
270
|
-
saveConfig(config, projectRoot);
|
|
271
|
-
} else if (walletAddress) {
|
|
272
|
-
// Fresh project: bootstrap minimal config using address from keystore
|
|
273
|
-
const bootstrapConfig: CLIConfig = {
|
|
274
|
-
...CONFIG_DEFAULTS,
|
|
275
|
-
mode: 'testnet', // safe default for new projects
|
|
276
|
-
address: walletAddress.toLowerCase(),
|
|
277
|
-
wallet: 'auto',
|
|
278
|
-
version: '1.0',
|
|
279
|
-
};
|
|
280
|
-
saveConfig(bootstrapConfig, projectRoot);
|
|
281
|
-
output.info('Created .actp/config.json (testnet, auto wallet).');
|
|
282
|
-
}
|
|
283
|
-
} catch {
|
|
284
|
-
// Config setup is best-effort — publish works without it
|
|
285
|
-
}
|
|
286
|
-
addToGitignore(projectRoot);
|
|
287
|
-
|
|
288
|
-
// ================================================================
|
|
289
|
-
// ALWAYS activate on testnet (one command, both networks per SPEC)
|
|
290
|
-
// ================================================================
|
|
291
|
-
let testnetTxHash: string | undefined;
|
|
292
|
-
const activationSpinner = output.spinner('Activating on testnet...');
|
|
293
|
-
try {
|
|
294
|
-
testnetTxHash = await activateOnTestnet(
|
|
295
|
-
projectRoot, configHash, cid,
|
|
296
|
-
regParams.endpoint, regParams.serviceDescriptors, output,
|
|
297
|
-
);
|
|
298
|
-
activationSpinner.stop(true);
|
|
299
|
-
if (testnetTxHash) {
|
|
300
|
-
output.success(`Testnet activation: ${testnetTxHash}`);
|
|
301
|
-
} else {
|
|
302
|
-
output.info('Testnet: already up-to-date.');
|
|
303
|
-
}
|
|
304
|
-
} catch (activationError) {
|
|
305
|
-
activationSpinner.stop(false);
|
|
306
|
-
// Save testnet pending as fallback — will activate on first testnet payment
|
|
307
|
-
savePendingPublish({ ...pendingData, network: 'base-sepolia' });
|
|
308
|
-
output.warning(
|
|
309
|
-
`Testnet activation failed: ${(activationError as Error).message}\n` +
|
|
310
|
-
' Saved as pending — will activate on first testnet payment.'
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Always save mainnet pending (lazy — activates on first mainnet payment)
|
|
315
|
-
savePendingPublish({ ...pendingData, network: 'base-mainnet' });
|
|
316
|
-
|
|
317
|
-
// Update AGIRAILS.md frontmatter
|
|
318
|
-
const updatedFrontmatter = {
|
|
319
|
-
...(frontmatter as Record<string, unknown>),
|
|
320
|
-
config_hash: configHash,
|
|
321
|
-
config_cid: cid,
|
|
322
|
-
published_at: new Date().toISOString(),
|
|
323
|
-
...(arweaveTxId ? { arweave_tx: arweaveTxId } : {}),
|
|
324
|
-
};
|
|
325
|
-
const updatedContent = serializeAgirailsMd(updatedFrontmatter, body);
|
|
326
|
-
writeFileSync(resolvedPath, updatedContent, 'utf-8');
|
|
327
|
-
|
|
328
|
-
// Output results
|
|
329
|
-
output.result(
|
|
330
|
-
{
|
|
331
|
-
configHash,
|
|
332
|
-
cid,
|
|
333
|
-
arweaveTxId: arweaveTxId || null,
|
|
334
|
-
pendingPublish: true,
|
|
335
|
-
testnetActivated: !!testnetTxHash,
|
|
336
|
-
...(testnetTxHash ? { testnetTxHash } : {}),
|
|
337
|
-
},
|
|
338
|
-
{ quietKey: 'configHash' }
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
output.blank();
|
|
342
|
-
output.success('Config published to IPFS and saved locally.');
|
|
343
|
-
|
|
344
|
-
if (testnetTxHash) {
|
|
345
|
-
output.print('');
|
|
346
|
-
output.success('Testnet: activated on-chain.');
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
output.print('');
|
|
350
|
-
output.print('Mainnet: on-chain activation will happen on your first payment.');
|
|
351
|
-
output.print('');
|
|
352
|
-
output.print('Next steps:');
|
|
353
|
-
output.print(' - Verify config: actp diff');
|
|
354
|
-
output.print(' - Make a payment: your first mainnet payment activates the agent on-chain');
|
|
355
|
-
|
|
356
|
-
// Warn if placeholder endpoint
|
|
357
|
-
if (!frontmatter.endpoint || frontmatter.endpoint === PENDING_ENDPOINT) {
|
|
358
|
-
output.print('');
|
|
359
|
-
output.warning('No endpoint in AGIRAILS.md — using placeholder URL.');
|
|
360
|
-
output.print(' Update when your agent is deployed:');
|
|
361
|
-
output.print(' 1. Add "endpoint: https://your-agent.com/webhook" to AGIRAILS.md');
|
|
362
|
-
output.print(' 2. Run: actp publish');
|
|
363
|
-
}
|
|
364
|
-
} catch (error) {
|
|
365
|
-
spinner.stop(false);
|
|
366
|
-
throw error;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// ============================================================================
|
|
371
|
-
// Testnet Activation
|
|
372
|
-
// ============================================================================
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Activate agent on testnet during `actp publish`.
|
|
376
|
-
*
|
|
377
|
-
* SPEC v4 Step 3: activation + mint test USDC in a single gasless UserOp.
|
|
378
|
-
* Always mints test USDC regardless of scenario (SPEC: "always on testnet").
|
|
379
|
-
*
|
|
380
|
-
* @returns Transaction hash of the UserOp, or undefined if already up-to-date
|
|
381
|
-
*/
|
|
382
|
-
async function activateOnTestnet(
|
|
383
|
-
projectRoot: string,
|
|
384
|
-
configHash: string,
|
|
385
|
-
cid: string,
|
|
386
|
-
endpoint: string,
|
|
387
|
-
serviceDescriptors: import('../../types/agent').ServiceDescriptor[],
|
|
388
|
-
output: Output,
|
|
389
|
-
): Promise<string | undefined> {
|
|
390
|
-
const { resolvePrivateKey } = await import('../../wallet/keystore');
|
|
391
|
-
const { ethers } = await import('ethers');
|
|
392
|
-
const { getNetwork } = await import('../../config/networks');
|
|
393
|
-
const { AutoWalletProvider } = await import('../../wallet/AutoWalletProvider');
|
|
394
|
-
const { buildActivationBatch, buildTestnetMintBatch } = await import('../../wallet/aa/TransactionBatcher');
|
|
395
|
-
const { getOnChainAgentState, detectLazyPublishScenario } = await import('../../ACTPClient');
|
|
396
|
-
|
|
397
|
-
const privateKey = await resolvePrivateKey(projectRoot, { network: 'testnet' });
|
|
398
|
-
if (!privateKey) {
|
|
399
|
-
throw new Error('No wallet found. Cannot activate on testnet.');
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const networkConfig = getNetwork('base-sepolia');
|
|
403
|
-
if (!networkConfig.aa || !networkConfig.contracts.agentRegistry) {
|
|
404
|
-
throw new Error('Testnet AA or AgentRegistry not configured.');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const provider = new ethers.JsonRpcProvider(networkConfig.rpcUrl);
|
|
408
|
-
const signer = new ethers.Wallet(privateKey, provider);
|
|
409
|
-
|
|
410
|
-
const autoWallet = await AutoWalletProvider.create({
|
|
411
|
-
signer,
|
|
412
|
-
provider,
|
|
413
|
-
chainId: networkConfig.chainId,
|
|
414
|
-
actpKernelAddress: networkConfig.contracts.actpKernel,
|
|
415
|
-
bundler: {
|
|
416
|
-
primaryUrl: networkConfig.aa.bundlerUrls.coinbase,
|
|
417
|
-
backupUrl: networkConfig.aa.bundlerUrls.pimlico,
|
|
418
|
-
},
|
|
419
|
-
paymaster: {
|
|
420
|
-
primaryUrl: networkConfig.aa.paymasterUrls.coinbase,
|
|
421
|
-
backupUrl: networkConfig.aa.paymasterUrls.pimlico,
|
|
422
|
-
},
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
const smartWalletAddress = autoWallet.getAddress();
|
|
426
|
-
output.info(`Smart Wallet: ${smartWalletAddress}`);
|
|
427
|
-
|
|
428
|
-
// Check on-chain state to determine activation scenario
|
|
429
|
-
const onChainState = await getOnChainAgentState(
|
|
430
|
-
provider, networkConfig.contracts.agentRegistry, smartWalletAddress
|
|
431
|
-
);
|
|
432
|
-
const scenario = detectLazyPublishScenario(onChainState, {
|
|
433
|
-
version: 1, configHash, cid, endpoint, serviceDescriptors, createdAt: new Date().toISOString(),
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
if (scenario === 'C' || scenario === 'none') {
|
|
437
|
-
// Already up-to-date on-chain — no activation needed
|
|
438
|
-
return undefined;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Build activation calls
|
|
442
|
-
const activationCalls = buildActivationBatch({
|
|
443
|
-
scenario,
|
|
444
|
-
agentRegistryAddress: networkConfig.contracts.agentRegistry,
|
|
445
|
-
cid,
|
|
446
|
-
configHash,
|
|
447
|
-
listed: true,
|
|
448
|
-
...(scenario === 'A' ? { endpoint, serviceDescriptors } : {}),
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
// Always mint test USDC on testnet (SPEC: "always")
|
|
452
|
-
const mintCalls = buildTestnetMintBatch(
|
|
453
|
-
networkConfig.contracts.usdc,
|
|
454
|
-
smartWalletAddress,
|
|
455
|
-
'1000000000', // 1000 USDC
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
const allCalls = [...activationCalls, ...mintCalls];
|
|
459
|
-
const txRequests = allCalls.map((c) => ({
|
|
460
|
-
to: c.target,
|
|
461
|
-
data: c.data,
|
|
462
|
-
value: c.value.toString(),
|
|
463
|
-
}));
|
|
464
|
-
|
|
465
|
-
output.info(`Submitting ${allCalls.length}-call UserOp...`);
|
|
466
|
-
const receipt = await autoWallet.sendBatchTransaction(txRequests);
|
|
467
|
-
|
|
468
|
-
if (!receipt.success) {
|
|
469
|
-
throw new Error(`Testnet activation UserOp failed: ${receipt.hash}`);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
output.success('Minted 1,000 test USDC to Smart Wallet');
|
|
473
|
-
return receipt.hash;
|
|
474
|
-
}
|
|
475
|
-
|
package/src/cli/commands/pull.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
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
|
-
}
|