// ElizaOS action: paid webpage extraction through ScanPay ReadClean (x402, Base USDC). // Requires: @elizaos/core, @x402/core, @x402/fetch, @x402/evm, viem. // Keep the private key outside prompts and logs; cap spending per day. import type { Action, Plugin } from "@elizaos/core"; import { x402Client } from "@x402/core/client"; import { wrapFetchWithPayment } from "@x402/fetch"; import { ExactEvmScheme } from "@x402/evm/exact/client"; import { privateKeyToAccount } from "viem/accounts"; const HOST = "https://scanpay-edge.alextaghiashvili.workers.dev"; const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; export function scanpayReadCleanPlugin(privateKey: `0x${string}` = "0x"): Plugin { const client = new x402Client(); client.register("eip155:8453", new ExactEvmScheme(privateKeyToAccount(privateKey))); const paidFetch = wrapFetchWithPayment(fetch, client); const action: Action = { name: "SCANPAY_READCLEAN", description: "Extract a public HTTPS page as clean Markdown. Costs 0.002 USDC per call.", similes: [], examples: [], validate: async () => true, handler: async (_runtime, message, _state, _options, callback) => { const match = message.content.text?.match(/^\/readclean\s+(https:\/\/\S+)\s*$/); if (!match) throw new Error("Use /readclean https://..."); const response = await paidFetch(HOST + "/api/v1/readclean", { method: "POST", redirect: "error", headers: {"Content-Type": "application/json", "X-ScanPay-Source": "eliza"}, body: JSON.stringify({url: match[1], format: "markdown"}), signal: AbortSignal.timeout(30000), }); if (!response.ok) throw new Error("ScanPay HTTP " + response.status + "; do not auto-retry a payment"); const data = await response.json() as {ok?: boolean; content?: string}; if (data.ok !== true || !data.content?.trim()) throw new Error("Extraction failed; reconcile the payment before retrying"); await callback?.({text: JSON.stringify(data)}); return true; }, }; return {name: "scanpay-readclean", description: "Paid webpage extraction through x402.", actions: [action]}; }