@agirails/sdk 2.3.3 → 2.4.0
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 +10 -12
- package/dist/ACTPClient.d.ts +80 -3
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +213 -57
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +13 -1
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +24 -3
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +9 -292
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/publish.d.ts +11 -3
- package/dist/cli/commands/publish.d.ts.map +1 -1
- package/dist/cli/commands/publish.js +319 -80
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.d.ts.map +1 -1
- package/dist/cli/commands/register.js +10 -0
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/utils/config.d.ts +3 -2
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js +9 -1
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/cli/utils/wallet.d.ts +31 -0
- package/dist/cli/utils/wallet.d.ts.map +1 -0
- package/dist/cli/utils/wallet.js +114 -0
- package/dist/cli/utils/wallet.js.map +1 -0
- package/dist/config/pendingPublish.d.ts +79 -0
- package/dist/config/pendingPublish.d.ts.map +1 -0
- package/dist/config/pendingPublish.js +167 -0
- package/dist/config/pendingPublish.js.map +1 -0
- package/dist/config/publishPipeline.d.ts +33 -0
- package/dist/config/publishPipeline.d.ts.map +1 -1
- package/dist/config/publishPipeline.js +33 -2
- package/dist/config/publishPipeline.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +2 -1
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
- package/dist/wallet/AutoWalletProvider.js +6 -2
- package/dist/wallet/AutoWalletProvider.js.map +1 -1
- package/dist/wallet/IWalletProvider.d.ts +4 -2
- package/dist/wallet/IWalletProvider.d.ts.map +1 -1
- package/dist/wallet/aa/TransactionBatcher.d.ts +54 -0
- package/dist/wallet/aa/TransactionBatcher.d.ts.map +1 -1
- package/dist/wallet/aa/TransactionBatcher.js +67 -1
- package/dist/wallet/aa/TransactionBatcher.js.map +1 -1
- package/package.json +1 -1
- package/src/ACTPClient.ts +265 -49
- package/src/adapters/BasicAdapter.ts +48 -12
- package/src/cli/commands/init.ts +7 -348
- package/src/cli/commands/publish.ts +354 -87
- package/src/cli/commands/register.ts +14 -0
- package/src/cli/utils/config.ts +11 -2
- package/src/cli/utils/wallet.ts +109 -0
- package/src/config/pendingPublish.ts +226 -0
- package/src/config/publishPipeline.ts +82 -1
- package/src/index.ts +8 -0
- package/src/wallet/AutoWalletProvider.ts +7 -2
- package/src/wallet/IWalletProvider.ts +4 -2
- package/src/wallet/aa/TransactionBatcher.ts +113 -0
package/README.md
CHANGED
|
@@ -436,15 +436,14 @@ npm run test:coverage
|
|
|
436
436
|
```typescript
|
|
437
437
|
import { ServiceDirectory, request, provide } from '@agirails/sdk';
|
|
438
438
|
|
|
439
|
-
// Register a service
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
//
|
|
447
|
-
const providers = directory.find({ capabilities: ['gpt-4'] });
|
|
439
|
+
// Register a provider for a service
|
|
440
|
+
const { serviceDirectory } = require('@agirails/sdk');
|
|
441
|
+
// serviceDirectory is an in-memory, per-process singleton
|
|
442
|
+
// Provider registers automatically when calling provide()
|
|
443
|
+
|
|
444
|
+
// Find providers for a service
|
|
445
|
+
const providers = serviceDirectory.findProviders('text-gen');
|
|
446
|
+
// Returns string[] of provider addresses
|
|
448
447
|
```
|
|
449
448
|
|
|
450
449
|
### Level 1 - Agent Framework
|
|
@@ -456,11 +455,10 @@ import { Agent, AgentConfig } from '@agirails/sdk';
|
|
|
456
455
|
const agent = new Agent({
|
|
457
456
|
name: 'my-agent',
|
|
458
457
|
network: 'testnet',
|
|
459
|
-
services: ['text-generation'],
|
|
460
458
|
});
|
|
461
459
|
|
|
462
|
-
//
|
|
463
|
-
agent.
|
|
460
|
+
// Register services via agent.provide()
|
|
461
|
+
agent.provide('text-generation', async (job) => {
|
|
464
462
|
return { result: `Processed: ${job.input}` };
|
|
465
463
|
});
|
|
466
464
|
|
package/dist/ACTPClient.d.ts
CHANGED
|
@@ -11,10 +11,9 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
14
|
-
* // Create client
|
|
14
|
+
* // Create client (auto-detects wallet from .actp/keystore.json or env vars)
|
|
15
15
|
* const client = await ACTPClient.create({
|
|
16
16
|
* mode: 'mock',
|
|
17
|
-
* requesterAddress: '0x1234...',
|
|
18
17
|
* });
|
|
19
18
|
*
|
|
20
19
|
* // Basic API - simplest approach
|
|
@@ -34,6 +33,7 @@
|
|
|
34
33
|
* const tx = await client.advanced.getTransaction(txId);
|
|
35
34
|
* ```
|
|
36
35
|
*/
|
|
36
|
+
import { ethers } from 'ethers';
|
|
37
37
|
import { IACTPRuntime } from './runtime/IACTPRuntime';
|
|
38
38
|
import { BasicAdapter } from './adapters/BasicAdapter';
|
|
39
39
|
import { StandardAdapter } from './adapters/StandardAdapter';
|
|
@@ -42,6 +42,42 @@ import { UnifiedPayParams, UnifiedPayResult } from './types/adapter';
|
|
|
42
42
|
import { EASHelper, EASConfig } from './protocol/EASHelper';
|
|
43
43
|
import { ReputationReporter } from './erc8004/ReputationReporter';
|
|
44
44
|
import { IWalletProvider } from './wallet/IWalletProvider';
|
|
45
|
+
import { SmartWalletCall } from './wallet/aa/constants';
|
|
46
|
+
import { ActivationScenario } from './wallet/aa/TransactionBatcher';
|
|
47
|
+
import { PendingPublish } from './config/pendingPublish';
|
|
48
|
+
/**
|
|
49
|
+
* Validates that a state directory path is safe to use.
|
|
50
|
+
*
|
|
51
|
+
* SECURITY: Prevents path traversal attacks by ensuring:
|
|
52
|
+
* 1. No '..' components in the path
|
|
53
|
+
* 2. No symbolic links that could escape the intended directory
|
|
54
|
+
* 3. Path resolves to a location within home directory or current working directory
|
|
55
|
+
*
|
|
56
|
+
* @param stateDirectory - The directory path to validate
|
|
57
|
+
* @throws Error if path is unsafe
|
|
58
|
+
*/
|
|
59
|
+
/** On-chain agent state from AgentRegistry. */
|
|
60
|
+
export interface OnChainAgentState {
|
|
61
|
+
registeredAt: bigint;
|
|
62
|
+
configHash: string;
|
|
63
|
+
listed: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Read the on-chain agent state from AgentRegistry.
|
|
67
|
+
* Returns registeredAt, configHash, and listed fields.
|
|
68
|
+
*/
|
|
69
|
+
export declare function getOnChainAgentState(provider: ethers.JsonRpcProvider, registryAddress: string, agentAddress: string): Promise<OnChainAgentState>;
|
|
70
|
+
/**
|
|
71
|
+
* Detect the lazy publish activation scenario.
|
|
72
|
+
*
|
|
73
|
+
* Decision matrix:
|
|
74
|
+
* - A: Not registered + has pending → first-time activation
|
|
75
|
+
* - B1: Registered + pending hash != on-chain hash + not listed → re-publish + list
|
|
76
|
+
* - B2: Registered + pending hash != on-chain hash + already listed → re-publish only
|
|
77
|
+
* - C: Pending hash == on-chain hash → stale pending, delete it
|
|
78
|
+
* - none: No pending publish file
|
|
79
|
+
*/
|
|
80
|
+
export declare function detectLazyPublishScenario(onChainState: OnChainAgentState, pendingPublish: PendingPublish | null): ActivationScenario;
|
|
45
81
|
/**
|
|
46
82
|
* Supported modes for ACTPClient.
|
|
47
83
|
*
|
|
@@ -358,6 +394,34 @@ export declare class ACTPClient {
|
|
|
358
394
|
* @internal
|
|
359
395
|
*/
|
|
360
396
|
private readonly walletProvider?;
|
|
397
|
+
/**
|
|
398
|
+
* Lazy Publish: Current activation scenario.
|
|
399
|
+
* Set during create(), consumed during first payACTPBatched().
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
402
|
+
private lazyScenario;
|
|
403
|
+
/**
|
|
404
|
+
* Lazy Publish: Cached pending publish data.
|
|
405
|
+
* @internal
|
|
406
|
+
*/
|
|
407
|
+
private pendingPublish;
|
|
408
|
+
/**
|
|
409
|
+
* AgentRegistry address (for lazy activation calls).
|
|
410
|
+
* @internal
|
|
411
|
+
*/
|
|
412
|
+
private agentRegistryAddress?;
|
|
413
|
+
/**
|
|
414
|
+
* Network identifier (e.g. 'base-sepolia', 'base-mainnet').
|
|
415
|
+
* Used for chain-scoped pending-publish file operations.
|
|
416
|
+
* @internal
|
|
417
|
+
*/
|
|
418
|
+
private networkId?;
|
|
419
|
+
/**
|
|
420
|
+
* Whether the pending publish config is stale (AGIRAILS.md changed since last publish).
|
|
421
|
+
* When true, getActivationCalls() returns empty to prevent stale config going on-chain.
|
|
422
|
+
* @internal
|
|
423
|
+
*/
|
|
424
|
+
private pendingIsStale;
|
|
361
425
|
/**
|
|
362
426
|
* Private constructor - use ACTPClient.create() factory method.
|
|
363
427
|
*/
|
|
@@ -657,7 +721,7 @@ export declare class ACTPClient {
|
|
|
657
721
|
* @example
|
|
658
722
|
* ```typescript
|
|
659
723
|
* // Register a custom x402 adapter
|
|
660
|
-
* client.registerAdapter(new X402Adapter(
|
|
724
|
+
* client.registerAdapter(new X402Adapter(requesterAddress, { expectedNetwork, transferFn }));
|
|
661
725
|
* ```
|
|
662
726
|
*/
|
|
663
727
|
registerAdapter(adapter: IAdapter): void;
|
|
@@ -705,6 +769,19 @@ export declare class ACTPClient {
|
|
|
705
769
|
* or sending custom batched transactions.
|
|
706
770
|
*/
|
|
707
771
|
getWalletProvider(): IWalletProvider | undefined;
|
|
772
|
+
/**
|
|
773
|
+
* Get activation calls for lazy publish.
|
|
774
|
+
*
|
|
775
|
+
* Returns SmartWalletCall[] to prepend to the first payment UserOp,
|
|
776
|
+
* plus an onSuccess callback that deletes pending-publish.json.
|
|
777
|
+
*
|
|
778
|
+
* Returns empty calls if no activation is needed (scenario C/none).
|
|
779
|
+
* @internal
|
|
780
|
+
*/
|
|
781
|
+
getActivationCalls(): {
|
|
782
|
+
calls: SmartWalletCall[];
|
|
783
|
+
onSuccess: () => void;
|
|
784
|
+
};
|
|
708
785
|
/**
|
|
709
786
|
* Non-blocking drift detection for AGIRAILS.md config.
|
|
710
787
|
* Checks if local AGIRAILS.md matches on-chain config hash.
|
package/dist/ACTPClient.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ACTPClient.d.ts","sourceRoot":"","sources":["../src/ACTPClient.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ACTPClient.d.ts","sourceRoot":"","sources":["../src/ACTPClient.ts"],"names":[],"mappings":"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"}
|
package/dist/ACTPClient.js
CHANGED
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
14
|
* ```typescript
|
|
15
|
-
* // Create client
|
|
15
|
+
* // Create client (auto-detects wallet from .actp/keystore.json or env vars)
|
|
16
16
|
* const client = await ACTPClient.create({
|
|
17
17
|
* mode: 'mock',
|
|
18
|
-
* requesterAddress: '0x1234...',
|
|
19
18
|
* });
|
|
20
19
|
*
|
|
21
20
|
* // Basic API - simplest approach
|
|
@@ -59,7 +58,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
59
58
|
return result;
|
|
60
59
|
};
|
|
61
60
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
|
-
exports.ACTPClient = void 0;
|
|
61
|
+
exports.ACTPClient = exports.detectLazyPublishScenario = exports.getOnChainAgentState = void 0;
|
|
63
62
|
const path = __importStar(require("path"));
|
|
64
63
|
const os = __importStar(require("os"));
|
|
65
64
|
const fs = __importStar(require("fs"));
|
|
@@ -76,29 +75,15 @@ const ReputationReporter_1 = require("./erc8004/ReputationReporter");
|
|
|
76
75
|
const networks_1 = require("./config/networks");
|
|
77
76
|
const EOAWalletProvider_1 = require("./wallet/EOAWalletProvider");
|
|
78
77
|
const AutoWalletProvider_1 = require("./wallet/AutoWalletProvider");
|
|
78
|
+
const TransactionBatcher_1 = require("./wallet/aa/TransactionBatcher");
|
|
79
|
+
const pendingPublish_1 = require("./config/pendingPublish");
|
|
79
80
|
const Logger_1 = require("./utils/Logger");
|
|
80
|
-
|
|
81
|
-
// Security: Path Validation
|
|
82
|
-
// ============================================================================
|
|
83
|
-
/**
|
|
84
|
-
* Validates that a state directory path is safe to use.
|
|
85
|
-
*
|
|
86
|
-
* SECURITY: Prevents path traversal attacks by ensuring:
|
|
87
|
-
* 1. No '..' components in the path
|
|
88
|
-
* 2. No symbolic links that could escape the intended directory
|
|
89
|
-
* 3. Path resolves to a location within home directory or current working directory
|
|
90
|
-
*
|
|
91
|
-
* @param stateDirectory - The directory path to validate
|
|
92
|
-
* @throws Error if path is unsafe
|
|
93
|
-
*/
|
|
81
|
+
const ZERO_HASH = '0x' + '0'.repeat(64);
|
|
94
82
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* Uses minimal ABI fragment to avoid importing the full AgentRegistry class.
|
|
99
|
-
* Checks registeredAt field of AgentProfile struct (> 0 means registered).
|
|
83
|
+
* Read the on-chain agent state from AgentRegistry.
|
|
84
|
+
* Returns registeredAt, configHash, and listed fields.
|
|
100
85
|
*/
|
|
101
|
-
async function
|
|
86
|
+
async function getOnChainAgentState(provider, registryAddress, agentAddress) {
|
|
102
87
|
const contract = new ethers_1.ethers.Contract(registryAddress, [
|
|
103
88
|
'function getAgent(address agentAddress) view returns ' +
|
|
104
89
|
'(tuple(address agentAddress, string did, string endpoint, bytes32[] serviceTypes, ' +
|
|
@@ -107,8 +92,42 @@ async function checkRegistration(provider, registryAddress, agentAddress) {
|
|
|
107
92
|
'uint256 updatedAt, bool isActive, bytes32 configHash, string configCID, bool listed))',
|
|
108
93
|
], provider);
|
|
109
94
|
const profile = await contract.getAgent(agentAddress);
|
|
110
|
-
return
|
|
95
|
+
return {
|
|
96
|
+
registeredAt: profile.registeredAt,
|
|
97
|
+
configHash: profile.configHash,
|
|
98
|
+
listed: profile.listed,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
exports.getOnChainAgentState = getOnChainAgentState;
|
|
102
|
+
/**
|
|
103
|
+
* Detect the lazy publish activation scenario.
|
|
104
|
+
*
|
|
105
|
+
* Decision matrix:
|
|
106
|
+
* - A: Not registered + has pending → first-time activation
|
|
107
|
+
* - B1: Registered + pending hash != on-chain hash + not listed → re-publish + list
|
|
108
|
+
* - B2: Registered + pending hash != on-chain hash + already listed → re-publish only
|
|
109
|
+
* - C: Pending hash == on-chain hash → stale pending, delete it
|
|
110
|
+
* - none: No pending publish file
|
|
111
|
+
*/
|
|
112
|
+
function detectLazyPublishScenario(onChainState, pendingPublish) {
|
|
113
|
+
if (!pendingPublish)
|
|
114
|
+
return 'none';
|
|
115
|
+
const isRegistered = onChainState.registeredAt > 0n;
|
|
116
|
+
const pendingHash = pendingPublish.configHash;
|
|
117
|
+
const onChainHash = onChainState.configHash;
|
|
118
|
+
if (!isRegistered) {
|
|
119
|
+
// Not registered — scenario A: full activation
|
|
120
|
+
return 'A';
|
|
121
|
+
}
|
|
122
|
+
// Registered — check if pending hash differs from on-chain
|
|
123
|
+
if (pendingHash !== onChainHash) {
|
|
124
|
+
// Config differs — need to publish
|
|
125
|
+
return onChainState.listed ? 'B2' : 'B1';
|
|
126
|
+
}
|
|
127
|
+
// Hash matches — stale pending
|
|
128
|
+
return 'C';
|
|
111
129
|
}
|
|
130
|
+
exports.detectLazyPublishScenario = detectLazyPublishScenario;
|
|
112
131
|
function validateStateDirectory(stateDirectory) {
|
|
113
132
|
// Check for path traversal characters
|
|
114
133
|
if (stateDirectory.includes('..')) {
|
|
@@ -224,13 +243,34 @@ class ACTPClient {
|
|
|
224
243
|
/**
|
|
225
244
|
* Private constructor - use ACTPClient.create() factory method.
|
|
226
245
|
*/
|
|
227
|
-
constructor(runtime, requesterAddress, info, easHelper, erc8004Bridge, reputationReporter, walletProvider, contractAddresses) {
|
|
246
|
+
constructor(runtime, requesterAddress, info, easHelper, erc8004Bridge, reputationReporter, walletProvider, contractAddresses, lazyScenario = 'none', pendingPublish = null, agentRegistryAddress, networkId) {
|
|
247
|
+
/**
|
|
248
|
+
* Lazy Publish: Current activation scenario.
|
|
249
|
+
* Set during create(), consumed during first payACTPBatched().
|
|
250
|
+
* @internal
|
|
251
|
+
*/
|
|
252
|
+
this.lazyScenario = 'none';
|
|
253
|
+
/**
|
|
254
|
+
* Lazy Publish: Cached pending publish data.
|
|
255
|
+
* @internal
|
|
256
|
+
*/
|
|
257
|
+
this.pendingPublish = null;
|
|
258
|
+
/**
|
|
259
|
+
* Whether the pending publish config is stale (AGIRAILS.md changed since last publish).
|
|
260
|
+
* When true, getActivationCalls() returns empty to prevent stale config going on-chain.
|
|
261
|
+
* @internal
|
|
262
|
+
*/
|
|
263
|
+
this.pendingIsStale = false;
|
|
228
264
|
this.runtime = runtime;
|
|
229
265
|
this.info = info;
|
|
230
266
|
this.easHelper = easHelper;
|
|
231
267
|
this.reputationReporter = reputationReporter;
|
|
232
268
|
this.walletProvider = walletProvider;
|
|
233
|
-
this.
|
|
269
|
+
this.lazyScenario = lazyScenario;
|
|
270
|
+
this.pendingPublish = pendingPublish;
|
|
271
|
+
this.agentRegistryAddress = agentRegistryAddress;
|
|
272
|
+
this.networkId = networkId;
|
|
273
|
+
this.basic = new BasicAdapter_1.BasicAdapter(runtime, requesterAddress, easHelper, walletProvider, contractAddresses, this);
|
|
234
274
|
this.standard = new StandardAdapter_1.StandardAdapter(runtime, requesterAddress, easHelper);
|
|
235
275
|
// Initialize registry and router
|
|
236
276
|
this.registry = new AdapterRegistry_1.AdapterRegistry();
|
|
@@ -284,6 +324,10 @@ class ACTPClient {
|
|
|
284
324
|
let walletProvider;
|
|
285
325
|
let requesterAddress;
|
|
286
326
|
let contractAddresses;
|
|
327
|
+
let lazyScenario = 'none';
|
|
328
|
+
let lazyPending = null;
|
|
329
|
+
let registryAddr;
|
|
330
|
+
let networkId;
|
|
287
331
|
// If custom runtime provided, use it directly
|
|
288
332
|
if (config.runtime) {
|
|
289
333
|
// Custom runtime: requesterAddress is mandatory
|
|
@@ -301,15 +345,17 @@ class ACTPClient {
|
|
|
301
345
|
// Initialize runtime based on mode
|
|
302
346
|
switch (config.mode) {
|
|
303
347
|
case 'mock': {
|
|
304
|
-
// Mock mode: requesterAddress is
|
|
305
|
-
if (
|
|
306
|
-
|
|
348
|
+
// Mock mode: requesterAddress is optional (auto-generate if missing)
|
|
349
|
+
if (config.requesterAddress) {
|
|
350
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(config.requesterAddress)) {
|
|
351
|
+
throw new Error(`Invalid requesterAddress: "${config.requesterAddress}". ` +
|
|
352
|
+
'Must be a valid Ethereum address (0x-prefixed, 40 hex chars)');
|
|
353
|
+
}
|
|
354
|
+
requesterAddress = config.requesterAddress;
|
|
307
355
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
'Must be a valid Ethereum address (0x-prefixed, 40 hex chars)');
|
|
356
|
+
else {
|
|
357
|
+
requesterAddress = ethers_1.ethers.Wallet.createRandom().address;
|
|
311
358
|
}
|
|
312
|
-
requesterAddress = config.requesterAddress;
|
|
313
359
|
// SECURITY FIX: Enhanced path validation to prevent path traversal attacks
|
|
314
360
|
if (config.stateDirectory) {
|
|
315
361
|
validateStateDirectory(config.stateDirectory);
|
|
@@ -323,12 +369,25 @@ class ACTPClient {
|
|
|
323
369
|
}
|
|
324
370
|
case 'testnet':
|
|
325
371
|
case 'mainnet': {
|
|
326
|
-
//
|
|
372
|
+
// Auto-detect private key from keystore / env var if not provided
|
|
327
373
|
if (!config.privateKey) {
|
|
328
|
-
|
|
374
|
+
const { resolvePrivateKey } = await Promise.resolve().then(() => __importStar(require('./wallet/keystore')));
|
|
375
|
+
const resolved = await resolvePrivateKey(config.stateDirectory);
|
|
376
|
+
if (resolved) {
|
|
377
|
+
config = { ...config, privateKey: resolved };
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
throw new Error(`No wallet found for ${config.mode} mode.\n\n` +
|
|
381
|
+
'Provide a private key via one of:\n' +
|
|
382
|
+
' 1. ACTP_KEY_PASSWORD env var + .actp/keystore.json (recommended)\n' +
|
|
383
|
+
' 2. ACTP_PRIVATE_KEY env var\n' +
|
|
384
|
+
' 3. privateKey option in ACTPClient.create()\n' +
|
|
385
|
+
' 4. Run "actp publish" to generate a wallet automatically');
|
|
386
|
+
}
|
|
329
387
|
}
|
|
330
388
|
// Map mode to network config
|
|
331
389
|
const network = config.mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
|
|
390
|
+
networkId = network;
|
|
332
391
|
const networkConfig = (0, networks_1.getNetwork)(network);
|
|
333
392
|
// Default RPC URL from network config if not provided
|
|
334
393
|
const rpcUrl = config.rpcUrl ?? networkConfig.rpcUrl;
|
|
@@ -338,7 +397,8 @@ class ACTPClient {
|
|
|
338
397
|
}
|
|
339
398
|
// Create ethers provider and signer
|
|
340
399
|
const provider = new ethers_1.ethers.JsonRpcProvider(rpcUrl);
|
|
341
|
-
const
|
|
400
|
+
const privateKey = config.privateKey; // Guaranteed by auto-detect above
|
|
401
|
+
const signer = new ethers_1.ethers.Wallet(privateKey, provider);
|
|
342
402
|
// ====================================================================
|
|
343
403
|
// AIP-12: Wallet Provider Selection
|
|
344
404
|
// ====================================================================
|
|
@@ -374,40 +434,66 @@ class ACTPClient {
|
|
|
374
434
|
backupUrl: networkConfig.aa.paymasterUrls.pimlico,
|
|
375
435
|
},
|
|
376
436
|
});
|
|
377
|
-
// Check AgentRegistry
|
|
437
|
+
// Check AgentRegistry + Lazy Publish scenario
|
|
378
438
|
const smartWalletAddress = autoWallet.getAddress();
|
|
379
|
-
|
|
439
|
+
registryAddr = config.contracts?.agentRegistry
|
|
380
440
|
?? networkConfig.contracts.agentRegistry;
|
|
381
|
-
|
|
382
|
-
|
|
441
|
+
// Load pending publish (may be null) — chain-scoped
|
|
442
|
+
try {
|
|
443
|
+
lazyPending = (0, pendingPublish_1.loadPendingPublish)(network);
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
// Ignore file read errors
|
|
447
|
+
}
|
|
448
|
+
let useAutoWallet = false;
|
|
449
|
+
if (registryAddr) {
|
|
383
450
|
try {
|
|
384
|
-
|
|
451
|
+
const onChainState = await getOnChainAgentState(provider, registryAddr, smartWalletAddress);
|
|
452
|
+
lazyScenario = detectLazyPublishScenario(onChainState, lazyPending);
|
|
453
|
+
// Scenario C: stale pending — delete immediately
|
|
454
|
+
if (lazyScenario === 'C') {
|
|
455
|
+
(0, pendingPublish_1.deletePendingPublish)(network);
|
|
456
|
+
lazyPending = null;
|
|
457
|
+
lazyScenario = 'none';
|
|
458
|
+
}
|
|
459
|
+
// Gate: configHash != ZERO || hasPendingPublish → use AutoWallet
|
|
460
|
+
const hasOnChainConfig = onChainState.configHash !== ZERO_HASH;
|
|
461
|
+
const hasPendingPublish = lazyPending !== null;
|
|
462
|
+
if (hasOnChainConfig || hasPendingPublish) {
|
|
463
|
+
useAutoWallet = true;
|
|
464
|
+
}
|
|
385
465
|
}
|
|
386
466
|
catch {
|
|
387
|
-
// Registry check failed (e.g. RPC down)
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
|
|
391
|
-
|
|
467
|
+
// Registry check failed (e.g. RPC down).
|
|
468
|
+
// Fail-open only if pending publish exists (agent did `actp publish` → legitimate intent).
|
|
469
|
+
// Fail-closed otherwise to prevent unregistered agents getting free gas.
|
|
470
|
+
if (lazyPending) {
|
|
471
|
+
useAutoWallet = true;
|
|
472
|
+
Logger_1.sdkLogger.warn('AgentRegistry check failed, but pending publish found — proceeding with AA.');
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
Logger_1.sdkLogger.warn('AgentRegistry check failed and no pending publish — falling back to EOA.');
|
|
476
|
+
}
|
|
392
477
|
}
|
|
393
478
|
}
|
|
394
479
|
else {
|
|
395
480
|
// No registry deployed — skip check (early testnet)
|
|
396
|
-
|
|
481
|
+
useAutoWallet = true;
|
|
397
482
|
}
|
|
398
|
-
if (
|
|
483
|
+
if (useAutoWallet) {
|
|
399
484
|
walletProvider = autoWallet;
|
|
400
485
|
requesterAddress = smartWalletAddress;
|
|
401
486
|
}
|
|
402
487
|
else {
|
|
403
|
-
// Not
|
|
404
|
-
Logger_1.sdkLogger.warn('Agent not
|
|
488
|
+
// Not published and no pending — fall back to EOA with warning
|
|
489
|
+
Logger_1.sdkLogger.warn('Agent not published on AgentRegistry and no pending publish found. ' +
|
|
405
490
|
'Falling back to EOA wallet (gas not sponsored). ' +
|
|
406
|
-
'Run "actp
|
|
491
|
+
'Run "actp publish" for gas-free transactions.');
|
|
407
492
|
walletProvider = new EOAWalletProvider_1.EOAWalletProvider(signer, networkConfig.chainId);
|
|
408
|
-
// Force signer.address — config.requesterAddress may be the Smart Wallet
|
|
409
|
-
// address (set by `actp init --wallet auto`), which would be wrong for EOA.
|
|
410
493
|
requesterAddress = signer.address;
|
|
494
|
+
// Reset since we're not using auto wallet
|
|
495
|
+
lazyScenario = 'none';
|
|
496
|
+
lazyPending = null;
|
|
411
497
|
}
|
|
412
498
|
}
|
|
413
499
|
else {
|
|
@@ -472,8 +558,29 @@ class ACTPClient {
|
|
|
472
558
|
stateDirectory,
|
|
473
559
|
walletTier: walletProvider?.getWalletInfo().tier,
|
|
474
560
|
};
|
|
475
|
-
//
|
|
476
|
-
|
|
561
|
+
// Staleness check: recompute hash if AGIRAILS.md exists and we have a pending publish
|
|
562
|
+
let pendingIsStale = false;
|
|
563
|
+
if (lazyPending && lazyScenario !== 'none' && lazyScenario !== 'C') {
|
|
564
|
+
try {
|
|
565
|
+
const mdPath = path.join(process.cwd(), 'AGIRAILS.md');
|
|
566
|
+
if (fs.existsSync(mdPath)) {
|
|
567
|
+
const { computeConfigHash } = await Promise.resolve().then(() => __importStar(require('./config/agirailsmd')));
|
|
568
|
+
const content = fs.readFileSync(mdPath, 'utf-8');
|
|
569
|
+
const { configHash: currentHash } = computeConfigHash(content);
|
|
570
|
+
if (currentHash !== lazyPending.configHash) {
|
|
571
|
+
pendingIsStale = true;
|
|
572
|
+
Logger_1.sdkLogger.warn('AGIRAILS.md changed since last publish. Activation skipped. ' +
|
|
573
|
+
'Run "actp publish" to update.');
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
// Best-effort: staleness check should not block operation
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// Pass wallet provider, contract addresses, and lazy publish state to constructor
|
|
582
|
+
const client = new ACTPClient(runtime, normalizedAddress, info, easHelper, erc8004Bridge, reputationReporter, walletProvider, contractAddresses, lazyScenario, lazyPending, registryAddr, networkId);
|
|
583
|
+
client.pendingIsStale = pendingIsStale;
|
|
477
584
|
// Drift detection: non-blocking check for AGIRAILS.md sync status
|
|
478
585
|
if (config.mode !== 'mock') {
|
|
479
586
|
client.checkConfigDrift(config).catch(() => {
|
|
@@ -844,7 +951,7 @@ class ACTPClient {
|
|
|
844
951
|
* @example
|
|
845
952
|
* ```typescript
|
|
846
953
|
* // Register a custom x402 adapter
|
|
847
|
-
* client.registerAdapter(new X402Adapter(
|
|
954
|
+
* client.registerAdapter(new X402Adapter(requesterAddress, { expectedNetwork, transferFn }));
|
|
848
955
|
* ```
|
|
849
956
|
*/
|
|
850
957
|
registerAdapter(adapter) {
|
|
@@ -900,6 +1007,48 @@ class ACTPClient {
|
|
|
900
1007
|
getWalletProvider() {
|
|
901
1008
|
return this.walletProvider;
|
|
902
1009
|
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Get activation calls for lazy publish.
|
|
1012
|
+
*
|
|
1013
|
+
* Returns SmartWalletCall[] to prepend to the first payment UserOp,
|
|
1014
|
+
* plus an onSuccess callback that deletes pending-publish.json.
|
|
1015
|
+
*
|
|
1016
|
+
* Returns empty calls if no activation is needed (scenario C/none).
|
|
1017
|
+
* @internal
|
|
1018
|
+
*/
|
|
1019
|
+
getActivationCalls() {
|
|
1020
|
+
if (this.lazyScenario === 'none' || this.lazyScenario === 'C' || !this.agentRegistryAddress) {
|
|
1021
|
+
return { calls: [], onSuccess: () => { } };
|
|
1022
|
+
}
|
|
1023
|
+
// Staleness check: AGIRAILS.md changed since last publish → skip activation
|
|
1024
|
+
if (this.pendingIsStale) {
|
|
1025
|
+
return { calls: [], onSuccess: () => { } };
|
|
1026
|
+
}
|
|
1027
|
+
const pending = this.pendingPublish;
|
|
1028
|
+
if (!pending) {
|
|
1029
|
+
return { calls: [], onSuccess: () => { } };
|
|
1030
|
+
}
|
|
1031
|
+
// Build activation batch params
|
|
1032
|
+
const params = {
|
|
1033
|
+
scenario: this.lazyScenario,
|
|
1034
|
+
agentRegistryAddress: this.agentRegistryAddress,
|
|
1035
|
+
cid: pending.cid,
|
|
1036
|
+
configHash: pending.configHash,
|
|
1037
|
+
listed: true,
|
|
1038
|
+
};
|
|
1039
|
+
// For scenario A, extract registration params from pending publish
|
|
1040
|
+
if (this.lazyScenario === 'A') {
|
|
1041
|
+
params.endpoint = pending.endpoint;
|
|
1042
|
+
params.serviceDescriptors = pending.serviceDescriptors;
|
|
1043
|
+
}
|
|
1044
|
+
const calls = (0, TransactionBatcher_1.buildActivationBatch)(params);
|
|
1045
|
+
const onSuccess = () => {
|
|
1046
|
+
(0, pendingPublish_1.deletePendingPublish)(this.networkId);
|
|
1047
|
+
this.lazyScenario = 'none';
|
|
1048
|
+
this.pendingPublish = null;
|
|
1049
|
+
};
|
|
1050
|
+
return { calls, onSuccess };
|
|
1051
|
+
}
|
|
903
1052
|
/**
|
|
904
1053
|
* Non-blocking drift detection for AGIRAILS.md config.
|
|
905
1054
|
* Checks if local AGIRAILS.md matches on-chain config hash.
|
|
@@ -913,7 +1062,14 @@ class ACTPClient {
|
|
|
913
1062
|
// Look for AGIRAILS.md in cwd
|
|
914
1063
|
const agirailsMdPath = join(process.cwd(), 'AGIRAILS.md');
|
|
915
1064
|
if (!existsSync(agirailsMdPath)) {
|
|
916
|
-
|
|
1065
|
+
// No local file — try cached hash from pending-publish.json
|
|
1066
|
+
const { loadPendingPublish: loadPP } = await Promise.resolve().then(() => __importStar(require('./config/pendingPublish')));
|
|
1067
|
+
const driftNetwork = config.mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
|
|
1068
|
+
const pp = loadPP(driftNetwork);
|
|
1069
|
+
if (pp) {
|
|
1070
|
+
Logger_1.sdkLogger.info('[AGIRAILS] No AGIRAILS.md found, using cached config hash from pending-publish.json');
|
|
1071
|
+
}
|
|
1072
|
+
return;
|
|
917
1073
|
}
|
|
918
1074
|
const network = config.mode === 'testnet' ? 'base-sepolia' : 'base-mainnet';
|
|
919
1075
|
const networkConfig = (0, networks_1.getNetwork)(network);
|