UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

289 lines (280 loc) 13.1 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.x402ActionProvider = exports.X402ActionProvider = void 0; const zod_1 = require("zod"); const actionProvider_1 = require("../actionProvider"); const actionDecorator_1 = require("../actionDecorator"); const schemas_1 = require("./schemas"); const wallet_providers_1 = require("../../wallet-providers"); const axios_1 = __importDefault(require("axios")); const x402_axios_1 = require("x402-axios"); const SUPPORTED_NETWORKS = ["base-mainnet", "base-sepolia"]; /** * X402ActionProvider provides actions for making HTTP requests, with optional x402 payment handling. */ class X402ActionProvider extends actionProvider_1.ActionProvider { /** * Creates a new instance of X402ActionProvider. * Initializes the provider with x402 capabilities. */ constructor() { super("x402", []); /** * Checks if the action provider supports the given network. * * @param network - The network to check support for * @returns True if the network is supported, false otherwise */ this.supportsNetwork = (network) => network.protocolFamily === "evm" && SUPPORTED_NETWORKS.includes(network.networkId); } /** * Makes a basic HTTP request to an API endpoint. * * @param walletProvider - The wallet provider to use for potential payments * @param args - The request parameters including URL, method, headers, and body * @returns A JSON string containing the response or error details */ async makeHttpRequest(walletProvider, args) { try { const response = await axios_1.default.request({ url: args.url, method: args.method ?? "GET", headers: args.headers ?? undefined, data: args.body, validateStatus: status => status === 402 || (status >= 200 && status < 300), }); if (response.status !== 402) { return JSON.stringify({ success: true, url: args.url, method: args.method, status: response.status, data: response.data, }, null, 2); } return JSON.stringify({ status: "error_402_payment_required", acceptablePaymentOptions: response.data.accepts, nextSteps: [ "Inform the user that the requested server replied with a 402 Payment Required response.", `The payment options are: ${response.data.accepts.map(option => `${option.asset} ${option.maxAmountRequired} ${option.network}`).join(", ")}`, "Ask the user if they want to retry the request with payment.", `Use retry_http_request_with_x402 to retry the request with payment.`, ], }); } catch (error) { return this.handleHttpError(error, args.url); } } /** * Retries a request with x402 payment after receiving a 402 response. * * @param walletProvider - The wallet provider to use for making the payment * @param args - The request parameters including URL, method, headers, body, and payment option * @returns A JSON string containing the response with payment details or error information */ async retryWithX402(walletProvider, args) { try { // Make the request with payment handling const account = walletProvider.toSigner(); const paymentSelector = (accepts) => { const { scheme, network, maxAmountRequired, asset } = args.selectedPaymentOption; let paymentRequirements = accepts.find(accept => accept.scheme === scheme && accept.network === network && accept.maxAmountRequired <= maxAmountRequired && accept.asset === asset); if (paymentRequirements) { return paymentRequirements; } paymentRequirements = accepts.find(accept => accept.scheme === scheme && accept.network === network && accept.maxAmountRequired <= maxAmountRequired && accept.asset === asset); if (paymentRequirements) { return paymentRequirements; } return accepts[0]; }; const api = (0, x402_axios_1.withPaymentInterceptor)(axios_1.default.create({}), account, paymentSelector); const response = await api.request({ url: args.url, method: args.method ?? "GET", headers: args.headers ?? undefined, data: args.body, }); // Check for payment proof const paymentProof = response.headers["x-payment-response"] ? (0, x402_axios_1.decodeXPaymentResponse)(response.headers["x-payment-response"]) : null; return JSON.stringify({ status: "success", data: response.data, message: "Request completed successfully with payment", details: { url: args.url, method: args.method, paymentUsed: { network: args.selectedPaymentOption.network, asset: args.selectedPaymentOption.asset, amount: args.selectedPaymentOption.maxAmountRequired, }, paymentProof: paymentProof ? { transaction: paymentProof.transaction, network: paymentProof.network, payer: paymentProof.payer, } : null, }, }); } catch (error) { return this.handleHttpError(error, args.url); } } /** * Makes an HTTP request with automatic x402 payment handling. * * @param walletProvider - The wallet provider to use for automatic payments * @param args - The request parameters including URL, method, headers, and body * @returns A JSON string containing the response with optional payment details or error information */ async makeHttpRequestWithX402(walletProvider, args) { try { const account = walletProvider.toSigner(); const api = (0, x402_axios_1.withPaymentInterceptor)(axios_1.default.create({}), account); const response = await api.request({ url: args.url, method: args.method ?? "GET", headers: args.headers ?? undefined, data: args.body, }); // Check for payment proof const paymentProof = response.headers["x-payment-response"] ? (0, x402_axios_1.decodeXPaymentResponse)(response.headers["x-payment-response"]) : null; return JSON.stringify({ success: true, message: "Request completed successfully (payment handled automatically if required)", url: args.url, method: args.method, status: response.status, data: response.data, paymentProof: paymentProof ? { transaction: paymentProof.transaction, network: paymentProof.network, payer: paymentProof.payer, } : null, }, null, 2); } catch (error) { return this.handleHttpError(error, args.url); } } /** * Helper method to handle HTTP errors consistently. * * @param error - The axios error to handle * @param url - The URL that was being accessed when the error occurred * @returns A JSON string containing formatted error details */ handleHttpError(error, url) { if (error.response) { return JSON.stringify({ error: true, message: `HTTP ${error.response.status} error when accessing ${url}`, details: error.response.data?.error || error.response.statusText, suggestion: "Check if the URL is correct and the API is available.", }, null, 2); } if (error.request) { return JSON.stringify({ error: true, message: `Network error when accessing ${url}`, details: error.message, suggestion: "Check your internet connection and verify the API endpoint is accessible.", }, null, 2); } return JSON.stringify({ error: true, message: `Error making request to ${url}`, details: error.message, suggestion: "Please check the request parameters and try again.", }, null, 2); } } exports.X402ActionProvider = X402ActionProvider; __decorate([ (0, actionDecorator_1.CreateAction)({ name: "make_http_request", description: ` Makes a basic HTTP request to an API endpoint. If the endpoint requires payment (returns 402), it will return payment details that can be used with retry_http_request_with_x402. EXAMPLES: - Production API: make_http_request("https://api.example.com/weather") - Local development: make_http_request("http://localhost:3000/api/data") - Testing x402: make_http_request("http://localhost:3000/protected") If you receive a 402 Payment Required response, use retry_http_request_with_x402 to handle the payment.`, schema: schemas_1.HttpRequestSchema, }), __metadata("design:type", Function), __metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]), __metadata("design:returntype", Promise) ], X402ActionProvider.prototype, "makeHttpRequest", null); __decorate([ (0, actionDecorator_1.CreateAction)({ name: "retry_http_request_with_x402", description: ` Retries an HTTP request with x402 payment after receiving a 402 Payment Required response. This should be used after make_http_request returns a 402 response. EXAMPLE WORKFLOW: 1. First call make_http_request("http://localhost:3000/protected") 2. If you get a 402 response, use this action to retry with payment 3. Pass the entire original response to this action DO NOT use this action directly without first trying make_http_request!`, schema: schemas_1.RetryWithX402Schema, }), __metadata("design:type", Function), __metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]), __metadata("design:returntype", Promise) ], X402ActionProvider.prototype, "retryWithX402", null); __decorate([ (0, actionDecorator_1.CreateAction)({ name: "make_http_request_with_x402", description: ` ⚠️ WARNING: This action automatically handles payments without asking for confirmation! Only use this when explicitly told to skip the confirmation flow. For most cases, you should: 1. First try make_http_request 2. Then use retry_http_request_with_x402 if payment is required This action combines both steps into one, which means: - No chance to review payment details before paying - No confirmation step - Automatic payment processing EXAMPLES: - Production: make_http_request_with_x402("https://api.example.com/data") - Local dev: make_http_request_with_x402("http://localhost:3000/protected") Unless specifically instructed otherwise, prefer the two-step approach with make_http_request first.`, schema: schemas_1.DirectX402RequestSchema, }), __metadata("design:type", Function), __metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]), __metadata("design:returntype", Promise) ], X402ActionProvider.prototype, "makeHttpRequestWithX402", null); const x402ActionProvider = () => new X402ActionProvider(); exports.x402ActionProvider = x402ActionProvider;