@agirails/sdk 2.5.2 → 2.5.4

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.
Files changed (172) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +67 -22
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +12 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +30 -4
  8. package/dist/adapters/BasicAdapter.js.map +1 -1
  9. package/dist/adapters/StandardAdapter.d.ts +20 -3
  10. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  11. package/dist/adapters/StandardAdapter.js +45 -11
  12. package/dist/adapters/StandardAdapter.js.map +1 -1
  13. package/dist/cli/commands/publish.js +16 -4
  14. package/dist/cli/commands/publish.js.map +1 -1
  15. package/dist/cli/commands/register.js +16 -4
  16. package/dist/cli/commands/register.js.map +1 -1
  17. package/dist/cli/commands/tx.js +31 -3
  18. package/dist/cli/commands/tx.js.map +1 -1
  19. package/dist/cli/utils/client.d.ts.map +1 -1
  20. package/dist/cli/utils/client.js +1 -0
  21. package/dist/cli/utils/client.js.map +1 -1
  22. package/dist/config/networks.d.ts +2 -2
  23. package/dist/config/networks.d.ts.map +1 -1
  24. package/dist/config/networks.js +27 -22
  25. package/dist/config/networks.js.map +1 -1
  26. package/dist/level0/request.d.ts.map +1 -1
  27. package/dist/level0/request.js +2 -1
  28. package/dist/level0/request.js.map +1 -1
  29. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  30. package/dist/runtime/BlockchainRuntime.js +11 -5
  31. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  32. package/dist/runtime/MockStateManager.d.ts.map +1 -1
  33. package/dist/runtime/MockStateManager.js +2 -1
  34. package/dist/runtime/MockStateManager.js.map +1 -1
  35. package/dist/utils/IPFSClient.d.ts +3 -1
  36. package/dist/utils/IPFSClient.d.ts.map +1 -1
  37. package/dist/utils/IPFSClient.js +27 -7
  38. package/dist/utils/IPFSClient.js.map +1 -1
  39. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  40. package/dist/wallet/AutoWalletProvider.js +52 -18
  41. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  42. package/dist/wallet/SmartWalletRouter.d.ts +116 -0
  43. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  44. package/dist/wallet/SmartWalletRouter.js +212 -0
  45. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  46. package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
  47. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  48. package/dist/wallet/aa/DualNonceManager.js +100 -5
  49. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  50. package/package.json +3 -6
  51. package/src/ACTPClient.ts +0 -1579
  52. package/src/abi/ACTPKernel.json +0 -1356
  53. package/src/abi/AgentRegistry.json +0 -915
  54. package/src/abi/ERC20.json +0 -40
  55. package/src/abi/EscrowVault.json +0 -134
  56. package/src/abi/IdentityRegistry.json +0 -316
  57. package/src/adapters/AdapterRegistry.ts +0 -173
  58. package/src/adapters/AdapterRouter.ts +0 -416
  59. package/src/adapters/BaseAdapter.ts +0 -498
  60. package/src/adapters/BasicAdapter.ts +0 -514
  61. package/src/adapters/IAdapter.ts +0 -292
  62. package/src/adapters/StandardAdapter.ts +0 -555
  63. package/src/adapters/X402Adapter.ts +0 -731
  64. package/src/adapters/index.ts +0 -60
  65. package/src/builders/DeliveryProofBuilder.ts +0 -327
  66. package/src/builders/QuoteBuilder.ts +0 -483
  67. package/src/builders/index.ts +0 -17
  68. package/src/cli/commands/balance.ts +0 -110
  69. package/src/cli/commands/batch.ts +0 -487
  70. package/src/cli/commands/config.ts +0 -231
  71. package/src/cli/commands/deploy-check.ts +0 -364
  72. package/src/cli/commands/deploy-env.ts +0 -120
  73. package/src/cli/commands/diff.ts +0 -141
  74. package/src/cli/commands/init.ts +0 -469
  75. package/src/cli/commands/mint.ts +0 -116
  76. package/src/cli/commands/pay.ts +0 -113
  77. package/src/cli/commands/publish.ts +0 -475
  78. package/src/cli/commands/pull.ts +0 -124
  79. package/src/cli/commands/register.ts +0 -247
  80. package/src/cli/commands/simulate.ts +0 -345
  81. package/src/cli/commands/time.ts +0 -302
  82. package/src/cli/commands/tx.ts +0 -448
  83. package/src/cli/commands/watch.ts +0 -211
  84. package/src/cli/index.ts +0 -134
  85. package/src/cli/utils/client.ts +0 -251
  86. package/src/cli/utils/config.ts +0 -389
  87. package/src/cli/utils/output.ts +0 -465
  88. package/src/cli/utils/wallet.ts +0 -109
  89. package/src/config/agirailsmd.ts +0 -262
  90. package/src/config/networks.ts +0 -275
  91. package/src/config/pendingPublish.ts +0 -237
  92. package/src/config/publishPipeline.ts +0 -359
  93. package/src/config/syncOperations.ts +0 -279
  94. package/src/erc8004/ERC8004Bridge.ts +0 -462
  95. package/src/erc8004/ReputationReporter.ts +0 -468
  96. package/src/erc8004/index.ts +0 -61
  97. package/src/errors/index.ts +0 -427
  98. package/src/index.ts +0 -364
  99. package/src/level0/Provider.ts +0 -117
  100. package/src/level0/ServiceDirectory.ts +0 -131
  101. package/src/level0/index.ts +0 -10
  102. package/src/level0/provide.ts +0 -132
  103. package/src/level0/request.ts +0 -432
  104. package/src/level1/Agent.ts +0 -1426
  105. package/src/level1/index.ts +0 -10
  106. package/src/level1/pricing/PriceCalculator.ts +0 -255
  107. package/src/level1/pricing/PricingStrategy.ts +0 -198
  108. package/src/level1/types/Job.ts +0 -179
  109. package/src/level1/types/Options.ts +0 -291
  110. package/src/level1/types/index.ts +0 -8
  111. package/src/protocol/ACTPKernel.ts +0 -808
  112. package/src/protocol/AgentRegistry.ts +0 -559
  113. package/src/protocol/DIDManager.ts +0 -629
  114. package/src/protocol/DIDResolver.ts +0 -554
  115. package/src/protocol/EASHelper.ts +0 -378
  116. package/src/protocol/EscrowVault.ts +0 -255
  117. package/src/protocol/EventMonitor.ts +0 -204
  118. package/src/protocol/MessageSigner.ts +0 -510
  119. package/src/protocol/ProofGenerator.ts +0 -339
  120. package/src/protocol/QuoteBuilder.ts +0 -15
  121. package/src/registry/AgentRegistryClient.ts +0 -202
  122. package/src/runtime/BlockchainRuntime.ts +0 -1015
  123. package/src/runtime/IACTPRuntime.ts +0 -306
  124. package/src/runtime/MockRuntime.ts +0 -1298
  125. package/src/runtime/MockStateManager.ts +0 -576
  126. package/src/runtime/index.ts +0 -25
  127. package/src/runtime/types/MockState.ts +0 -237
  128. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  129. package/src/storage/ArweaveClient.ts +0 -946
  130. package/src/storage/FilebaseClient.ts +0 -790
  131. package/src/storage/index.ts +0 -96
  132. package/src/storage/types.ts +0 -348
  133. package/src/types/adapter.ts +0 -310
  134. package/src/types/agent.ts +0 -79
  135. package/src/types/did.ts +0 -223
  136. package/src/types/eip712.ts +0 -175
  137. package/src/types/erc8004.ts +0 -293
  138. package/src/types/escrow.ts +0 -27
  139. package/src/types/index.ts +0 -17
  140. package/src/types/message.ts +0 -145
  141. package/src/types/state.ts +0 -87
  142. package/src/types/transaction.ts +0 -69
  143. package/src/types/x402.ts +0 -251
  144. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  145. package/src/utils/Helpers.ts +0 -688
  146. package/src/utils/IPFSClient.ts +0 -368
  147. package/src/utils/Logger.ts +0 -484
  148. package/src/utils/NonceManager.ts +0 -591
  149. package/src/utils/RateLimiter.ts +0 -534
  150. package/src/utils/ReceivedNonceTracker.ts +0 -567
  151. package/src/utils/SDKLifecycle.ts +0 -416
  152. package/src/utils/SecureNonce.ts +0 -78
  153. package/src/utils/Semaphore.ts +0 -276
  154. package/src/utils/UsedAttestationTracker.ts +0 -385
  155. package/src/utils/canonicalJson.ts +0 -38
  156. package/src/utils/circuitBreaker.ts +0 -324
  157. package/src/utils/computeTypeHash.ts +0 -48
  158. package/src/utils/fsSafe.ts +0 -80
  159. package/src/utils/index.ts +0 -80
  160. package/src/utils/retry.ts +0 -364
  161. package/src/utils/security.ts +0 -418
  162. package/src/utils/validation.ts +0 -540
  163. package/src/wallet/AutoWalletProvider.ts +0 -299
  164. package/src/wallet/EOAWalletProvider.ts +0 -69
  165. package/src/wallet/IWalletProvider.ts +0 -135
  166. package/src/wallet/aa/BundlerClient.ts +0 -274
  167. package/src/wallet/aa/DualNonceManager.ts +0 -173
  168. package/src/wallet/aa/PaymasterClient.ts +0 -174
  169. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  170. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  171. package/src/wallet/aa/constants.ts +0 -60
  172. package/src/wallet/keystore.ts +0 -240
@@ -1,132 +0,0 @@
1
- /**
2
- * provide() - Basic API for service providers
3
- *
4
- * The simplest way to provide a service on AGIRAILS.
5
- * Just write a function, and AGIRAILS handles the rest.
6
- *
7
- * @packageDocumentation
8
- */
9
-
10
- import { Agent } from '../level1/Agent';
11
- import { JobHandler } from '../level1/types/Job';
12
- import { ProvideOptions } from '../level1/types/Options';
13
- import { Provider } from './Provider';
14
- import { serviceDirectory } from './ServiceDirectory';
15
- import { sdkLogger } from '../utils/Logger';
16
-
17
- /**
18
- * Provide a service
19
- *
20
- * This is the simplest way to provide a service on AGIRAILS.
21
- * You provide a service name and a handler function, and AGIRAILS
22
- * takes care of wallet management, transactions, and payments.
23
- *
24
- * @param service - Service name (e.g., 'translation', 'echo')
25
- * @param handler - Job handler function
26
- * @param options - Optional configuration
27
- * @returns Provider interface for lifecycle control
28
- *
29
- * @example
30
- * ```typescript
31
- * // Simplest example
32
- * provide('echo', async (job) => {
33
- * return job.input;
34
- * });
35
- *
36
- * // With filtering
37
- * provide('translation', async (job) => {
38
- * const { text, from, to } = job.input;
39
- * return { translated: await translate(text, from, to) };
40
- * }, {
41
- * filter: { minBudget: 5 },
42
- * network: 'testnet'
43
- * });
44
- *
45
- * // With events
46
- * const provider = provide('image-gen', async (job) => {
47
- * return { imageUrl: await generateImage(job.input.prompt) };
48
- * });
49
- *
50
- * provider.on('payment:received', (amount) => {
51
- * console.log(`Earned ${amount} USDC!`);
52
- * });
53
- * ```
54
- */
55
- export function provide(
56
- service: string,
57
- handler: JobHandler,
58
- options?: ProvideOptions
59
- ): Provider {
60
- // Create an Agent instance under the hood
61
- const agent = new Agent({
62
- name: `Provider:${service}`,
63
- description: `Auto-generated provider for ${service}`,
64
- network: options?.network || 'mock',
65
- wallet: options?.wallet,
66
- stateDirectory: options?.stateDirectory,
67
- rpcUrl: options?.rpcUrl,
68
- behavior: {
69
- autoAccept: options?.autoAccept ?? true,
70
- },
71
- });
72
-
73
- // Register the service with filter options
74
- agent.provide(
75
- {
76
- name: service,
77
- filter: options?.filter,
78
- },
79
- handler
80
- );
81
-
82
- // Start the agent immediately (async)
83
- agent.start().then(() => {
84
- // Register in service directory after agent has started and has an address
85
- serviceDirectory.register(service, agent.address);
86
- }).catch((error) => {
87
- sdkLogger.error(`Failed to start provider for ${service}`, { error: error instanceof Error ? error.message : String(error) });
88
- });
89
-
90
- // Return Provider interface (adapter over Agent)
91
- const provider: Provider = {
92
- get status() {
93
- return agent.status as any;
94
- },
95
-
96
- get address() {
97
- return agent.address;
98
- },
99
-
100
- get balance() {
101
- return agent.balance;
102
- },
103
-
104
- pause() {
105
- agent.pause();
106
- },
107
-
108
- resume() {
109
- agent.resume();
110
- },
111
-
112
- async stop() {
113
- await agent.stop();
114
- serviceDirectory.unregister(service, agent.address);
115
- },
116
-
117
- on(event: any, handler: any) {
118
- agent.on(event, handler);
119
- },
120
-
121
- get stats() {
122
- return {
123
- jobsCompleted: agent.stats.jobsCompleted,
124
- jobsFailed: agent.stats.jobsFailed,
125
- totalEarned: agent.stats.totalEarned,
126
- averageJobTime: agent.stats.averageJobTime,
127
- };
128
- },
129
- };
130
-
131
- return provider;
132
- }
@@ -1,432 +0,0 @@
1
- /**
2
- * request() - Basic API for service requesters
3
- *
4
- * The simplest way to request a service from AGIRAILS.
5
- * Specify what you need and your budget, and AGIRAILS finds a provider.
6
- *
7
- * @packageDocumentation
8
- */
9
-
10
- import { ACTPClient } from '../ACTPClient';
11
- import { RequestOptions, RequestResult } from '../level1/types/Options';
12
- import { serviceDirectory } from './ServiceDirectory';
13
- import { NoProviderFoundError, TimeoutError, ValidationError } from '../errors';
14
- import { safeJSONParse, validateServiceName, isValidAddress } from '../utils/security';
15
- import { Logger } from '../utils/Logger';
16
- import { ethers } from 'ethers';
17
- import { resolvePrivateKey } from '../wallet/keystore';
18
-
19
- /**
20
- * Request a service
21
- *
22
- * This is the simplest way to request a service on AGIRAILS.
23
- * Specify the service name, input data, and budget, and AGIRAILS
24
- * handles finding a provider, creating the transaction, and delivering the result.
25
- *
26
- * @param service - Service name (e.g., 'translation', 'echo')
27
- * @param options - Request options (input, budget, etc.)
28
- * @returns Promise resolving to request result
29
- *
30
- * @throws {NoProviderFoundError} If no provider is found for the service
31
- * @throws {TimeoutError} If provider doesn't respond within timeout
32
- * @throws {ProviderRejectedError} If provider rejects the job
33
- * @throws {DeliveryFailedError} If provider fails to deliver result
34
- *
35
- * @example
36
- * ```typescript
37
- * // Simplest example
38
- * const { result } = await request('echo', {
39
- * input: 'Hello, AGIRAILS!',
40
- * budget: 1
41
- * });
42
- * console.log(result); // 'Hello, AGIRAILS!'
43
- *
44
- * // With progress callback
45
- * const { result } = await request('translation', {
46
- * input: { text: 'Hello', from: 'en', to: 'de' },
47
- * budget: 5,
48
- * onProgress: (status) => {
49
- * console.log(`${status.state}: ${status.progress}%`);
50
- * }
51
- * });
52
- *
53
- * // With specific provider
54
- * const { result } = await request('image-gen', {
55
- * input: { prompt: 'A beautiful sunset' },
56
- * budget: 10,
57
- * provider: '0x1234...abcd'
58
- * });
59
- * ```
60
- */
61
- export async function request(
62
- service: string,
63
- options: RequestOptions
64
- ): Promise<RequestResult> {
65
- const validatedService = validateServiceName(service);
66
-
67
- const logger = new Logger({ source: 'request' });
68
-
69
- // Find provider
70
- const provider = findProvider(validatedService, options.provider);
71
-
72
- if (!provider) {
73
- throw new NoProviderFoundError(validatedService, {
74
- availableProviders: serviceDirectory.findProviders(validatedService),
75
- });
76
- }
77
-
78
- let rpcUrl = options.rpcUrl;
79
- if (!rpcUrl && (options.network === 'testnet' || options.network === 'mainnet')) {
80
- const { getNetwork } = await import('../config/networks');
81
- const networkName = options.network === 'testnet' ? 'base-sepolia' : 'base-mainnet';
82
- const networkConfig = getNetwork(networkName);
83
- rpcUrl = networkConfig.rpcUrl;
84
- logger.info(`Using default RPC URL for ${networkName}: ${rpcUrl}`);
85
- }
86
-
87
- const resolvedKey = await resolveKeyIfNeeded(options.wallet, options.network, options.stateDirectory);
88
- const resolvedAddress = resolvedKey
89
- ? new ethers.Wallet(resolvedKey).address.toLowerCase()
90
- : undefined;
91
-
92
- const client = await ACTPClient.create({
93
- mode: options.network === 'testnet' ? 'testnet' : options.network === 'mainnet' ? 'mainnet' : 'mock',
94
- requesterAddress: resolvedAddress || getRequesterAddress(options.wallet),
95
- stateDirectory: options.stateDirectory,
96
- privateKey: resolvedKey || getPrivateKey(options.wallet),
97
- rpcUrl,
98
- });
99
-
100
- const deadline = calculateDeadline(options.deadline, options.timeout);
101
- const startTime = Date.now();
102
-
103
- try {
104
- const requesterAddress = resolvedAddress || getRequesterAddress(options.wallet);
105
- const amountWei = (options.budget * 1_000_000).toString(); // Convert to USDC wei (6 decimals)
106
-
107
- // In mock mode, ensure requester has enough funds
108
- if (client.runtime && 'mintTokens' in client.runtime) {
109
- const mockRuntime = client.runtime as any;
110
- const balance = await mockRuntime.getBalance(requesterAddress);
111
- const balanceBigInt = BigInt(balance);
112
- const amountBigInt = BigInt(amountWei);
113
-
114
- if (balanceBigInt < amountBigInt) {
115
- // Mint enough tokens (with some buffer)
116
- const toMint = (amountBigInt - balanceBigInt + BigInt(10_000_000)).toString(); // +10 USDC buffer
117
- await mockRuntime.mintTokens(requesterAddress, toMint);
118
- }
119
- }
120
-
121
- const serviceMetadata = JSON.stringify({
122
- service: validatedService,
123
- input: options.input,
124
- timestamp: Date.now(),
125
- });
126
-
127
- const txId = await client.runtime.createTransaction({
128
- provider,
129
- requester: requesterAddress,
130
- amount: amountWei,
131
- deadline,
132
- disputeWindow: options.disputeWindow ?? 172800,
133
- serviceDescription: serviceMetadata,
134
- });
135
-
136
- // Call onProgress if provided
137
- if (options.onProgress) {
138
- options.onProgress({
139
- state: 'initiated',
140
- progress: 10,
141
- message: 'Transaction created, waiting for provider...',
142
- });
143
- }
144
-
145
- // Poll for DELIVERED state (provider completes the job)
146
- const maxWaitTime = options.timeout ?? 300000; // 5 minutes default
147
- const pollInterval = 2000; // 2 seconds
148
- const maxAttempts = Math.floor(maxWaitTime / pollInterval);
149
-
150
- let tx = await client.runtime.getTransaction(txId);
151
- let attempts = 0;
152
-
153
- while (tx && tx.state !== 'DELIVERED' && tx.state !== 'SETTLED' && attempts < maxAttempts) {
154
- // Check for terminal states that indicate failure
155
- if (tx.state === 'CANCELLED' || tx.state === 'DISPUTED') {
156
- throw new Error(`Transaction ${tx.state.toLowerCase()}`);
157
- }
158
-
159
- // Update progress
160
- if (options.onProgress) {
161
- const progress = 10 + Math.min(80, (attempts / maxAttempts) * 80);
162
- options.onProgress({
163
- state: tx.state.toLowerCase() as any,
164
- progress,
165
- message: `Waiting for delivery (${tx.state})...`,
166
- });
167
- }
168
-
169
- // Wait and poll again
170
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
171
- tx = await client.runtime.getTransaction(txId);
172
- attempts++;
173
- }
174
-
175
- if (!tx || (tx.state !== 'DELIVERED' && tx.state !== 'SETTLED')) {
176
- // Auto-cancel on timeout if still in early state
177
- if (tx && (tx.state === 'INITIATED' || tx.state === 'COMMITTED')) {
178
- try {
179
- logger.warn('Transaction timed out, cancelling to release funds', {
180
- txId,
181
- state: tx.state,
182
- });
183
-
184
- if ('cancelTransaction' in client.runtime) {
185
- await (client.runtime as any).cancelTransaction(txId);
186
- logger.info('Transaction cancelled successfully', { txId });
187
-
188
- const error = new TimeoutError(maxWaitTime, `Transaction cancelled after timeout`);
189
- (error as any).txId = txId;
190
- (error as any).wasCancelled = true;
191
- throw error;
192
- } else {
193
- await client.runtime.transitionState(txId, 'CANCELLED');
194
- logger.info('Transaction cancelled successfully (via transitionState)', { txId });
195
-
196
- const error = new TimeoutError(maxWaitTime, `Transaction cancelled after timeout`);
197
- (error as any).txId = txId;
198
- (error as any).wasCancelled = true;
199
- throw error;
200
- }
201
- } catch (cancelError) {
202
- logger.error('Failed to cancel timed-out transaction', { txId }, cancelError as Error);
203
- }
204
- }
205
-
206
- const error = new TimeoutError(maxWaitTime, `waiting for service '${validatedService}' delivery`);
207
- (error as any).txId = txId;
208
- (error as any).currentState = tx?.state || 'UNKNOWN';
209
- throw error;
210
- }
211
-
212
- let deliveredResult: any = {};
213
- if (tx.deliveryProof) {
214
- const DELIVERY_PROOF_SCHEMA: Record<string, string> = {
215
- result: 'any',
216
- data: 'any',
217
- metadata: 'object',
218
- proof: 'string',
219
- timestamp: 'number',
220
- contentHash: 'string',
221
- txId: 'string',
222
- type: 'string',
223
- };
224
-
225
- const parsed = safeJSONParse(tx.deliveryProof, DELIVERY_PROOF_SCHEMA);
226
-
227
- if (parsed !== null) {
228
- deliveredResult = parsed;
229
- } else {
230
- deliveredResult = { data: tx.deliveryProof };
231
- logger.warn('Failed to parse delivery proof as JSON', { txId });
232
- }
233
- } else if (options.network === 'testnet' || options.network === 'mainnet') {
234
- logger.warn(
235
- 'Delivery proof retrieval not yet implemented for testnet/mainnet. ' +
236
- 'Result may be empty. Use ACTPClient with manual proof handling for production.',
237
- { txId, network: options.network }
238
- );
239
- }
240
-
241
- if (tx.state === 'DELIVERED' && tx.escrowId) {
242
- const disputeWindowEnd = (tx.completedAt ?? 0) + tx.disputeWindow;
243
- const currentTime = client.runtime.time.now();
244
-
245
- if (currentTime >= disputeWindowEnd) {
246
- const isMockMode = options.network !== 'testnet' && options.network !== 'mainnet';
247
-
248
- if (isMockMode) {
249
- try {
250
- await client.runtime.releaseEscrow(tx.escrowId);
251
- } catch (error) {
252
- // Ignore if already released or dispute window still active
253
- // This is non-critical for result delivery
254
- }
255
- } else {
256
- // For testnet/mainnet, log a warning about manual verification
257
- logger.warn(
258
- 'Auto-release disabled for non-mock networks. ' +
259
- 'Verify delivery proof and call releaseEscrow() manually, ' +
260
- 'or enable attestation verification in BlockchainRuntime.',
261
- { txId, escrowId: tx.escrowId, network: options.network }
262
- );
263
- }
264
- }
265
- }
266
-
267
- if (options.onProgress) {
268
- options.onProgress({
269
- state: 'settled',
270
- progress: 100,
271
- message: 'Transaction completed!',
272
- });
273
- }
274
-
275
- // Extract raw handler result from delivery proof wrapper
276
- // The delivery proof structure is: { type: 'delivery.proof', result: <handler_output>, ... }
277
- // We return the raw handler output as `result` for better DX
278
- //
279
- // IMPORTANT: We check for type === 'delivery.proof' which is the unique marker
280
- // set by ProofGenerator. This avoids false positives from handlers that happen
281
- // to return objects with result/contentHash/timestamp fields.
282
- const isDeliveryProofWrapper = deliveredResult !== null &&
283
- typeof deliveredResult === 'object' &&
284
- deliveredResult.type === 'delivery.proof' &&
285
- 'result' in deliveredResult;
286
- const rawResult = isDeliveryProofWrapper ? deliveredResult.result : deliveredResult;
287
-
288
- const result: RequestResult = {
289
- result: rawResult,
290
- transaction: {
291
- id: txId,
292
- provider,
293
- amount: options.budget,
294
- fee: options.budget * 0.01, // 1% ACTP fee
295
- duration: Date.now() - startTime,
296
- proof: tx.deliveryProof ?? '',
297
- },
298
- };
299
-
300
- return result;
301
- } catch (error) {
302
- // Better error handling
303
- if (error instanceof TimeoutError) {
304
- throw error;
305
- }
306
-
307
- // Wrap unknown errors
308
- throw new Error(`Request failed: ${error instanceof Error ? error.message : String(error)}`);
309
- }
310
- }
311
-
312
- /**
313
- * Resolve private key from keystore if wallet is auto/undefined and network is testnet/mainnet.
314
- * Returns undefined if wallet is explicitly set (caller should use getPrivateKey instead).
315
- */
316
- async function resolveKeyIfNeeded(
317
- wallet?: 'auto' | 'connect' | string | { privateKey: string },
318
- network?: string,
319
- stateDirectory?: string
320
- ): Promise<string | undefined> {
321
- if (wallet && wallet !== 'auto') return undefined; // explicit wallet, skip auto-detect
322
- if (network !== 'testnet' && network !== 'mainnet') return undefined;
323
- return resolvePrivateKey(stateDirectory, { network });
324
- }
325
-
326
- /**
327
- * Find provider for service
328
- *
329
- * @param service - Service name
330
- * @param providerOption - Provider selection strategy
331
- * @returns Provider address or undefined
332
- */
333
- function findProvider(
334
- service: string,
335
- providerOption?: string | 'any' | 'best' | 'cheapest'
336
- ): string | undefined {
337
- // If specific provider specified, use it
338
- if (providerOption && providerOption !== 'any' && providerOption !== 'best' && providerOption !== 'cheapest') {
339
- return providerOption;
340
- }
341
-
342
- // Otherwise, find from service directory
343
- const providers = serviceDirectory.findProviders(service);
344
-
345
- if (providers.length === 0) {
346
- return undefined;
347
- }
348
-
349
- // For MVP, just return the first provider
350
- // In V2, implement 'best' and 'cheapest' strategies
351
- return providers[0];
352
- }
353
-
354
- function getRequesterAddress(
355
- wallet?: 'auto' | 'connect' | string | { privateKey: string }
356
- ): string {
357
- if (!wallet || wallet === 'auto') {
358
- const hex = Buffer.from('requester').toString('hex');
359
- return '0x' + hex.padEnd(40, '0');
360
- }
361
-
362
- if (wallet === 'connect') {
363
- throw new Error('Browser wallet connection not yet implemented');
364
- }
365
-
366
- if (typeof wallet === 'string') {
367
- if (!isValidAddress(wallet)) {
368
- throw new ValidationError('wallet', `Invalid Ethereum address format: ${wallet}`);
369
- }
370
- return wallet.toLowerCase();
371
- }
372
-
373
- try {
374
- const walletInstance = new ethers.Wallet(wallet.privateKey);
375
- return walletInstance.address.toLowerCase();
376
- } catch (error) {
377
- throw new ValidationError('wallet.privateKey', 'Invalid private key format');
378
- }
379
- }
380
-
381
- function getPrivateKey(
382
- wallet?: 'auto' | 'connect' | string | { privateKey: string }
383
- ): string | undefined {
384
- if (!wallet || wallet === 'auto' || wallet === 'connect') {
385
- return undefined;
386
- }
387
-
388
- if (typeof wallet === 'string') {
389
- if (/^0x[0-9a-fA-F]{64}$/.test(wallet)) {
390
- try {
391
- new ethers.Wallet(wallet);
392
- return wallet;
393
- } catch {
394
- throw new ValidationError('wallet', 'Invalid private key format');
395
- }
396
- }
397
- return undefined;
398
- }
399
-
400
- if (wallet.privateKey) {
401
- try {
402
- new ethers.Wallet(wallet.privateKey);
403
- return wallet.privateKey;
404
- } catch {
405
- throw new ValidationError('wallet.privateKey', 'Invalid private key format');
406
- }
407
- }
408
-
409
- return undefined;
410
- }
411
-
412
- /**
413
- * Calculate deadline timestamp
414
- *
415
- * @param deadline - Deadline option (timestamp or Date)
416
- * @param timeout - Timeout in milliseconds
417
- * @returns Deadline timestamp (seconds)
418
- */
419
- function calculateDeadline(
420
- deadline?: number | Date,
421
- timeout: number = 300000 // 5 minutes default
422
- ): number {
423
- if (deadline) {
424
- if (deadline instanceof Date) {
425
- return Math.floor(deadline.getTime() / 1000);
426
- }
427
- return deadline;
428
- }
429
-
430
- // Default: now + timeout
431
- return Math.floor(Date.now() / 1000) + Math.floor(timeout / 1000);
432
- }