@botanary/agent 0.1.0-alpha.4 → 0.1.0-alpha.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/CHANGELOG.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.1.0-alpha.5
4
4
 
5
5
  - Add `prepareAction({ mandate, action })` for exact Base Sepolia transfers from a confirmed mandate projection.
6
+ - Allow a mandate with strict active on-chain state to prepare an action while its durable creation operation still awaits final settlement.
6
7
  - Return deterministic typed refusals for over-limit, wrong-recipient, inactive, expired, and revoked mandates before signing or transport.
7
8
  - Require a trusted per-runtime native gas ceiling for the mandate facade and preserve the existing exact action hash, nonce-lane, one-shot submission, and reconciliation checks.
8
9
 
@@ -962,6 +962,7 @@ export interface components {
962
962
  name: string;
963
963
  version: number;
964
964
  permissionId: string;
965
+ salt: string;
965
966
  grantExecutor: components["schemas"]["Address"];
966
967
  policy: components["schemas"]["PublicGrantPolicy"];
967
968
  compiled: components["schemas"]["PublicGrantCompiled"];
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var h=class extends Error{constructor(t,n,i=null,o=null){super(t);this.status=n;this.details=i;this.retryAfterSeconds=o;this.name="BotanaryApiError"}requestId;code;operationId;userOpHash;requirementId;ambiguous=!1};import{encodeFunctionData as E,encodePacked as ve,decodeFunctionData as F,parseAbi as U,getAddress as I,zeroAddress as Pe,zeroHash as Re}from"viem";import{entryPoint07Address as ee,getUserOperationHash as Se}from"viem/account-abstraction";import{publicKeyToAddress as Ie}from"viem/accounts";function d(){let r=new h("Botanary returned an invalid response. Nothing was signed.",0);throw r.code="invalid_response",r}function y(r){return typeof r!="object"||r===null||Array.isArray(r)?d():r}function p(r,e){(typeof r!="string"||!/^0x(?:[0-9a-fA-F]{2})*$/.test(r)||e!==void 0&&r.length!==2+2*e)&&d()}function w(r){(typeof r!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(r)||/^(?:bsk|ags|dcs|dca|dcr)_/.test(r))&&d()}function L(r){(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r)||BigInt(r)>=2n**256n)&&d()}function H(r){let e=y(r);return p(e.address,20),p(e.publicKey,65),(!e.publicKey.startsWith("0x04")||Ie(e.publicKey).toLowerCase()!==e.address.toLowerCase()||typeof e.fingerprint!="string"||!/^[A-Za-z0-9]{1,32}$/.test(e.fingerprint)||typeof e.backend!="string"||!/^[A-Za-z0-9_-]{1,64}$/.test(e.backend)||typeof e.createdAt!="string"||!Number.isFinite(Date.parse(e.createdAt)))&&d(),Object.freeze({address:e.address,publicKey:e.publicKey,fingerprint:e.fingerprint,backend:e.backend,createdAt:e.createdAt})}function x(r,e=256){if(typeof r!="string"||!/^(0|[1-9][0-9]{0,77}|0x[0-9a-fA-F]{1,64})$/.test(r))return d();let t=BigInt(r);return t>=2n**BigInt(e)?d():t}function C(r){let e=y(r);for(let t of["sender","entryPoint"])p(e[t],20);for(let t of["nonce","callGasLimit","verificationGasLimit","preVerificationGas","maxFeePerGas","maxPriorityFeePerGas"])x(e[t]);for(let t of["callData","signature"])p(e[t]);for(let t of["paymasterVerificationGasLimit","paymasterPostOpGasLimit"])e[t]!=null&&x(e[t],128);(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d();for(let t of["factory","paymaster"])e[t]!=null&&p(e[t],20);for(let t of["factoryData","paymasterData"])e[t]!=null&&p(e[t]);return r}function T(r){let e=y(r);p(e.payload,32),p(e.validator,20),(!Number.isSafeInteger(e.index)||Number(e.index)<0||Number(e.index)>=2**32)&&d();let t=y(e.authorization);return p(t.from,20),p(t.to,20),p(t.nonce,32),L(t.value),L(t.validAfter),L(t.validBefore),BigInt(t.validBefore)<=BigInt(t.validAfter)&&d(),{payload:e.payload,validator:e.validator,index:Number(e.index),authorization:{from:t.from,to:t.to,nonce:t.nonce,value:t.value,validAfter:t.validAfter,validBefore:t.validBefore}}}function z(r,e){let t=y(r);(t.requirementId!==e||typeof t.simulated!="boolean")&&d();let n={ready:["not_submitted"],pending_approval:["not_submitted"],submitted:["pending"],settled:["settled"],declined:["not_submitted","not_settled"]};return(typeof t.status!="string"||!Object.hasOwn(n,t.status)||!n[t.status].includes(String(t.paymentStatus)))&&d(),t.providerStatus!==void 0&&(!Number.isSafeInteger(t.providerStatus)||Number(t.providerStatus)<100||Number(t.providerStatus)>599)&&d(),t.txHash!==void 0&&p(t.txHash,32),t.status==="settled"&&p(t.txHash,32),t.status==="ready"?T(t.ready):t.ready!==void 0&&d(),t}var te=U(["function execute(bytes32 mode, bytes executionCalldata) payable"]),M=U(["function executeUnderGrant(bytes32 permissionId, address target, uint256 value, bytes callData)","function executeUnderGrantWithAllowance(bytes32 permissionId, address target, uint256 value, address token, uint256 allowance, bytes callData)"]),_e=U(["function transfer(address to, uint256 amount) returns (bool)"]),ne=U(["function swapExactIn(address tokenIn, address tokenOut, uint256 amountIn, uint256 minOut, address to) returns (uint256 amountOut)"]),Ee=BigInt("0x000100000000008bdaba73cd9815d79069c247eb4bda0000");function f(r,e){return typeof r=="string"&&r.toLowerCase()===e.toLowerCase()}function b(r,e=!0){if(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r))return d();let t=BigInt(r);return t>=2n**256n||e&&t===0n?d():t}function re(r){let e=y(r),t=["accountId","accountAddress","chainId","action","tokenAddress","amount","recipient","tokenOutAddress","maxSlippageBps"];return Object.keys(e).some(n=>!t.includes(n))&&d(),w(e.accountId),p(e.accountAddress,20),p(e.tokenAddress,20),b(e.amount),(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d(),e.action==="transfer"?(p(e.recipient,20),(e.tokenOutAddress!==void 0||e.maxSlippageBps!==void 0)&&d()):e.action==="swap"?(p(e.tokenOutAddress,20),(e.recipient!==void 0||e.maxSlippageBps!==void 0&&(!Number.isInteger(e.maxSlippageBps)||Number(e.maxSlippageBps)<1||Number(e.maxSlippageBps)>1e3))&&d()):d(),Object.freeze({...r,accountAddress:I(r.accountAddress),tokenAddress:I(r.tokenAddress),...r.recipient!==void 0?{recipient:I(r.recipient)}:{},...r.tokenOutAddress!==void 0?{tokenOutAddress:I(r.tokenOutAddress)}:{}})}function ie(r,e){if(w(r.delegationId),p(r.permissionId,32),p(r.executorAddress,20),b(r.maxGasCostWei),e.action==="swap"){let t=r;p(t.routerAddress,20),b(t.minAmountOut),t.verifyRoute!==void 0&&typeof t.verifyRoute!="function"&&d()}return Object.freeze({...r,executorAddress:I(r.executorAddress),...e.action==="swap"?{routerAddress:I(r.routerAddress)}:{}})}async function se(r,e,t){try{let n=y(r),i=y(n.execution),o=C(n.userOp),s=Object.freeze({...o,sender:I(o.sender),entryPoint:I(o.entryPoint)});p(n.userOpHash,32),(n.intentType!=="delegated_action"||y(n.simulation).willSucceed!==!0||i.accountId!==e.accountId||!f(i.accountAddress,e.accountAddress)||i.chainId!==e.chainId||i.action!==e.action||i.amount!==e.amount||!f(i.tokenAddress,e.tokenAddress)||i.delegationId!==t.delegationId||!f(i.permissionId,t.permissionId)||i.gasMethod!=="native"||!f(s.sender,e.accountAddress)||s.chainId!==e.chainId||!f(s.entryPoint,ee)||s.factory!=null||s.factoryData!=null||s.paymaster!=null||s.paymasterData!=null||s.paymasterVerificationGasLimit!=null||s.paymasterPostOpGasLimit!=null)&&d();let a=x(s.nonce);a>>64n!==Ee&&d();let c=x(s.callGasLimit,128),u=x(s.verificationGasLimit,128),l=x(s.preVerificationGas),k=x(s.maxFeePerGas,128),m=x(s.maxPriorityFeePerGas,128);(m>k||(c+u+l)*k>b(t.maxGasCostWei))&&d();let g=s.callData,v;if(e.action==="transfer"){(!f(i.recipient,e.recipient)||i.tokenOutAddress!==void 0||i.maxSlippageBps!==void 0)&&d();let A=f(e.tokenAddress,Pe);v=E({abi:M,functionName:"executeUnderGrant",args:[t.permissionId,A?e.recipient:e.tokenAddress,A?b(e.amount):0n,A?"0x":E({abi:_e,functionName:"transfer",args:[e.recipient,b(e.amount)]})]})}else{let A=t;(!f(i.tokenOutAddress,e.tokenOutAddress)||i.recipient!==void 0||i.maxSlippageBps!==(e.maxSlippageBps??50))&&d();let q=F({abi:te,data:g}).args[1],P=F({abi:M,data:`0x${q.slice(106)}`});P.functionName!=="executeUnderGrantWithAllowance"&&d();let S=P.args[5],_=Object.freeze({chainId:e.chainId,accountAddress:e.accountAddress,routerAddress:A.routerAddress,tokenAddress:e.tokenAddress,tokenOutAddress:e.tokenOutAddress,amount:e.amount,minAmountOut:A.minAmountOut,callData:S});if(A.verifyRoute)await A.verifyRoute(_)!==!0&&d();else{let J=F({abi:ne,data:S}),[Ae,we,xe,be,ke]=J.args;(!f(Ae,e.tokenAddress)||!f(we,e.tokenOutAddress)||xe!==b(e.amount)||be<b(A.minAmountOut)||!f(ke,e.accountAddress)||!f(E({abi:ne,functionName:"swapExactIn",args:J.args}),S))&&d()}v=E({abi:M,functionName:"executeUnderGrantWithAllowance",args:[t.permissionId,A.routerAddress,0n,e.tokenAddress,b(e.amount),S]})}let R=E({abi:te,functionName:"execute",args:[Re,ve(["address","uint256","bytes"],[t.executorAddress,0n,v])]});f(g,R)||d();let $=Se({entryPointAddress:ee,entryPointVersion:"0.7",chainId:e.chainId,userOperation:{sender:s.sender,nonce:a,callData:s.callData,callGasLimit:c,verificationGasLimit:u,preVerificationGas:l,maxFeePerGas:k,maxPriorityFeePerGas:m,signature:"0x"}});f($,n.userOpHash)||d();let G=Object.freeze({...e,delegationId:t.delegationId,permissionId:t.permissionId,gasMethod:"native",...e.action==="swap"?{maxSlippageBps:e.maxSlippageBps??50}:{}});return{userOp:Object.freeze({sender:s.sender,chainId:s.chainId,entryPoint:s.entryPoint,nonce:s.nonce,callData:s.callData,callGasLimit:s.callGasLimit,verificationGasLimit:s.verificationGasLimit,preVerificationGas:s.preVerificationGas,maxFeePerGas:s.maxFeePerGas,maxPriorityFeePerGas:s.maxPriorityFeePerGas,signature:"0x"}),userOpHash:$,execution:G}}catch{return d()}}function j(r,e){try{let t=y(r);if(w(t.id),!f(t.userOpHash,e)||!["pending","included","failed"].includes(String(t.status)))throw new Error;return t.txHash!=null&&p(t.txHash,32),t}catch{let t=new h("Botanary returned an invalid receipt. Reconcile using the original operation hash.",0);throw t.code="invalid_response",t.userOpHash=e,t.ambiguous=!0,t}}import{keccak256 as Le,toHex as ze,encodePacked as he,encodeAbiParameters as Fe}from"viem";import{randomBytes as Oe}from"node:crypto";import{hexToBytes as Be}from"viem";var ae="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function W(r){let e=0,t=0,n="";for(let i of r)for(e=e<<8|i,t+=8;t>=5;)n+=ae[e>>>t-5&31],t-=5;return t>0&&(n+=ae[e<<5-t&31]),n}var oe=3,de=5;function V(r){let e=Be(r);return W(e.slice(0,oe))}function K(){return W(Oe(de))}var ce=600*1e3,O=class{#r;#e;#d;#a;#i=null;constructor(e,t){this.#r=e,this.#e=t?.ttlMs??ce,this.#d=t?.now??Date.now,this.#a=t?.nonce??K}current(){let e=this.#d();return(!this.#i||this.#i.expiresAt<=e)&&this.#c(e),this.#s()}regenerate(){return this.#c(this.#d()),this.#s()}#c(e){this.#i={nonce:this.#a(),issuedAt:e,expiresAt:e+this.#e}}#s(){let e=this.#i;if(!e)throw new Error("unreachable: no pairing code minted");return{code:`${this.#r}-${e.nonce}`,fingerprint:this.#r,nonce:e.nonce,issuedAt:new Date(e.issuedAt).toISOString(),expiresAt:new Date(e.expiresAt).toISOString()}}};import Ne from"openapi-fetch";var ue=[{method:"post",path:"/agents/pair"},{method:"post",path:"/agents/session/nonce"},{method:"post",path:"/agents/session"},{method:"get",path:"/agents/me"},{method:"get",path:"/agents/requests"},{method:"post",path:"/agents/requests"},{method:"get",path:"/balance"},{method:"get",path:"/chains"},{method:"get",path:"/gas/methods"},{method:"get",path:"/account"},{method:"post",path:"/delegations/{delegationId}/actions"},{method:"post",path:"/delegations/{delegationId}/actions/exact"},{method:"post",path:"/userops"},{method:"get",path:"/userops/{userOpId}"},{method:"get",path:"/userops/by-hash/{chainId}/{userOpHash}"},{method:"get",path:"/apis/providers"},{method:"get",path:"/apis/providers/{providerId}"},{method:"get",path:"/apis/budget"},{method:"post",path:"/apis/calls/requirements"},{method:"get",path:"/apis/calls/requirements/{id}"},{method:"post",path:"/apis/calls/relay"},{method:"get",path:"/apis/calls/relay/{id}"}];var pe="https://botanary-canonical-path.invalid",le="(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})",ut=`${le}+`,He=new RegExp(`^${le}+$`),Ce=new RegExp("^(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*$"),Te=/^\.{1,2}$/,Ue=/[/\\#?]/,De=/[\u0000-\u001f\u007f]/,$e=6;function Ge(r){let e=r;for(let t=0;t<$e;t+=1){if(Te.test(e)||Ue.test(e)||De.test(e))return!0;if(!e.includes("%"))return!1;let n;try{n=decodeURIComponent(e)}catch{return!0}if(n===e)return!1;e=n}return!0}function ge(r){if(typeof r!="string"||r.length===0||!r.startsWith("/")||r.startsWith("//"))return null;let e=r.indexOf("?"),t=e===-1?r:r.slice(0,e),n=e===-1?"":r.slice(e),i=t.split("/");for(let s=1;s<i.length;s+=1){let a=i[s];if(!He.test(a)||Ge(a))return null}if(n!==""&&!Ce.test(n.slice(1)))return null;let o;try{o=new URL(t+n,pe)}catch{return null}return o.origin!==pe||o.hash!==""||o.pathname!==t||o.search!==n?null:{path:t,query:n,full:t+n}}var D=class{#r;constructor(e,t=fetch){let n=new URL(e);if(n.username||n.password||n.search||n.hash||n.pathname!=="/"||n.protocol!=="https:"&&!(n.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(n.hostname)))throw new Error("Agent API origin must be HTTPS, or HTTP on loopback.");this.#r=Ne({baseUrl:n.origin+"/v1",fetch:qe(t)})}get(e,t){return this.#e("get",e,void 0,t)}post(e,t,n){return this.#e("post",e,t,n)}async#e(e,t,n,i){let o=ge(t);if(!o?.path.startsWith("/v1/"))throw new Error("Invalid agent API path.");if(i!==void 0&&!/^ags_[a-f0-9]{64}$/.test(i))throw new Error("Expected an agent session credential.");let s=o.path.slice(3),a=ue.find(m=>m.method===e&&new RegExp("^"+m.path.replace(/\{[^}]+\}/g,"[A-Za-z0-9_-]+")+"$").test(s));if(!a)throw new Error("This route is outside the reviewed agent API.");let c={};a.path.split("/").forEach((m,g)=>{m.startsWith("{")&&(c[m.slice(1,-1)]=s.split("/")[g])});let u=Object.fromEntries(new URLSearchParams(o.query)),l=new AbortController,k=setTimeout(()=>l.abort(),3e4);try{let m=await this.#r.request(e,a.path,{params:{path:c,query:u},...n===void 0?{}:{body:n},headers:i?{authorization:`Bearer ${i}`}:{},credentials:"omit",redirect:"error",cache:"no-store",signal:l.signal,parseAs:"text"}),g=m.response,v=m.error;if(g.ok)try{v=m.data?JSON.parse(m.data):{}}catch{throw new Error("Invalid JSON response.")}if(!g.ok){let R=Z(Z(v)?.error),G=(typeof R?.message=="string"?R.message:`Agent request failed with ${g.status}.`).replace(/(?:bsk_(?:test|live)|ags|dcs|dca|dcr)_[A-Za-z0-9_-]+/g,"[redacted]").replace(/0x[a-fA-F0-9]{64,}/g,"[redacted]"),B=g.headers.get("retry-after"),A=B&&/^\d+(?:\.\d+)?$/.test(B)?Number(B):null,N=Z(R?.details),q=N&&Object.fromEntries(Object.entries(N).filter(([S,_])=>["missingModule","declineReason","chainId"].includes(S)&&(typeof _=="number"||typeof _=="string"&&/^[a-zA-Z0-9_-]{1,100}$/.test(_)))),P=new h(G.slice(0,1e3),g.status,q,A);throw P.requestId=me(g.headers.get("x-request-id")),P.code=me(R?.code)??"http_error",P.ambiguous=e==="post"&&g.status>=500,P}return v}catch(m){if(m instanceof h)throw m;let g=new h("The agent request did not complete. Check the existing operation before retrying.",0);throw g.code=l.signal.aborted?"timeout":"transport_error",g.ambiguous=e==="post",g}finally{clearTimeout(k)}}};function Z(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)?r:null}function me(r){return typeof r=="string"&&/^[a-zA-Z0-9_-]{1,128}$/.test(r)&&!/^(?:ags|bsk|dcs|dca|dcr)_/.test(r)?r:void 0}function qe(r){return async(e,t)=>{let n=e instanceof Request?e:new Request(e,t),i=n.signal,o=()=>{},s=new Promise((c,u)=>{o=()=>u(new Error("Request aborted.")),i.aborted?o():i.addEventListener("abort",o,{once:!0})}),a;try{let c=await Promise.race([r(n),s]);if(!c.body)return c;a=c.body.getReader();let u=[],l=0;for(;;){let g=await Promise.race([a.read(),s]);if(g.done)break;if(l+=g.value.byteLength,l>4*1024*1024)throw new Error("Response too large.");u.push(g.value)}let k=new Uint8Array(l),m=0;for(let g of u)k.set(g,m),m+=g.byteLength;return new Response(k,{status:c.status,statusText:c.statusText,headers:c.headers})}finally{i.removeEventListener("abort",o),a&&a.cancel().catch(()=>{})}}}import{recoverAddress as Me}from"viem";async function X(r,e,t,n,i){if(!Number.isFinite(t)||t<0||t>36e5)throw new Error("Invalid wait budget.");let o=n(),s=await r(e);for(;["pending","pending_approval","submitted","queued"].includes(String(s.status))&&n()-o<t;)await i(1500),s=await r(e);return s}var je=3e4,fe=300*1e3,ye="https://api.app.botanary.xyz",Y=class{#r;#e;#d;#a;#i=null;#c=null;#s=null;#u=null;#p=null;#n=0;#g;constructor(e,t={}){if(this.#r=e,this.#d=t.baseUrl??ye,this.#e=t.transport??new D(this.#d,t.fetch),this.#a=t.provenance??{},t.maxGasCostWei!==void 0&&(!/^[1-9][0-9]{0,77}$/.test(t.maxGasCostWei)||BigInt(t.maxGasCostWei)>=2n**256n))throw new Error("Use a positive integer agent gas ceiling in wei.");this.#g=t.maxGasCostWei??null}get apiBaseUrl(){return this.#d}get identityMaterialized(){return this.#i!==null||this.#u!==null}setProvenance(e){this.#a=e}replaceSigner(e){if(this.identityMaterialized)throw new Error("Cannot replace a materialized agent signer.");this.#r=e,this.reset()}async identity(){if(this.#i)return this.#i;if(this.#u)return this.#u;let e=this.#n,t=(async()=>{let n;try{n=await this.#r.ensure()}catch{throw this.#o()}if(e!==this.#n)throw this.#o();let i=H(n);return this.#i=i,i})();this.#u=t;try{return await t}finally{this.#u===t&&(this.#u=null)}}#o(){let e=new h("The agent signer could not complete the request.",0);return e.code="signer_error",e}async#l(e){p(e,32);let t=this.#n,n=await this.identity();if(t!==this.#n)throw this.#o();try{let i=await this.#r.signHash(e);p(i,65);let o=await Me({hash:e,signature:i});if(t!==this.#n||o.toLowerCase()!==n.address.toLowerCase())throw this.#o();return i}catch{throw this.#o()}}async pairingCode(){return(await this.#h()).current()}async regeneratePairingCode(){return(await this.#h()).regenerate()}async pair(){let e=await this.pairingCode(),t=Math.floor(Date.now()/1e3),n=await this.identity(),i=`${e.code}|${t}`,o=Le(ze(i));return await this.#e.post("/v1/agents/pair",{code:e.code,publicKey:n.publicKey,timestamp:t,signature:await this.#l(o),...this.#a.client?{client:this.#a.client}:{},...this.#a.profile?{profile:this.#a.profile}:{}}),e}async mintSession(){if(this.#p)return this.#p;let e=this.#n,t=(async()=>{let n=await this.identity(),i=y(await this.#e.post("/v1/agents/session/nonce",{address:n.address}));if(p(i.nonce,32),e!==this.#n)throw this.#o();let o=await this.#l(i.nonce),s=y(await this.#e.post("/v1/agents/session",{address:n.address,nonce:i.nonce,signature:o}));if((typeof s.token!="string"||!/^ags_[a-f0-9]{64}$/.test(s.token)||typeof s.expiresAt!="string"||!Number.isFinite(Date.parse(s.expiresAt))||Date.parse(s.expiresAt)<=Date.now()||Date.parse(s.expiresAt)>Date.now()+366e4)&&d(),e!==this.#n)throw this.#o();return this.#s={token:s.token,safeUntil:Date.parse(s.expiresAt)-je},s.token})();this.#p=t;try{return await t}finally{this.#p===t&&(this.#p=null)}}async ensureSession(){return this.#s&&this.#s.safeUntil>Date.now()?this.#s.token:this.mintSession()}async me(){return this.#t(e=>this.#e.get("/v1/agents/me",e))}async getBalance(){return this.#t(e=>this.#e.get("/v1/balance",e))}async listRequests(){return this.#t(e=>this.#e.get("/v1/agents/requests",e))}async listChains(){return this.#t(e=>this.#e.get("/v1/chains",e))}async getGasMethods(e){let t=new URLSearchParams;e.chainId!==void 0&&t.set("chainId",String(e.chainId)),e.accountId&&t.set("accountId",e.accountId),e.key&&t.set("key",e.key);let n=t.toString();return this.#t(i=>this.#e.get(`/v1/gas/methods${n?`?${n}`:""}`,i))}async getAccount(e){let t=new URLSearchParams;e.chainId!==void 0&&t.set("chainId",String(e.chainId));let n=t.toString();return this.#t(i=>this.#e.get(`/v1/account${n?`?${n}`:""}`,i))}async prepareExactAction(e,t){let n=re(e),i=ie(t,n),o=this.#n,s=await this.#t(u=>this.#e.post(`/v1/delegations/${i.delegationId}/actions/exact`,n,u),!1),a=await se(s,n,i),c;return Object.freeze({status:"prepared",userOpHash:a.userOpHash,execution:a.execution,submit:()=>(c||(c=(async()=>{if(o!==this.#n)throw this.#o();let u=await this.ensureSession();if(o!==this.#n)throw this.#o();return j(await this.spendUnderGrant(a.userOp,a.userOpHash,i.permissionId,u,"delegated_action"),a.userOpHash)})()),c),reconcile:()=>this.getUserOpByHash(n.chainId,a.userOpHash)})}async prepareAction(e){let{mandate:t,action:n}=structuredClone(e),i=/^[1-9][0-9]{0,77}$/.test(n.amountRaw)?BigInt(n.amountRaw):0n,o=t.policy.perActionMaxRaw&&/^[1-9][0-9]{0,77}$/.test(t.policy.perActionMaxRaw)?BigInt(t.policy.perActionMaxRaw):null,s=t.chainState.remaining?.find(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase())?.remainingRaw??null,a=u=>Object.freeze({status:"refused",reason:u,transactionSubmitted:!1,requestedAmountRaw:n.amountRaw,perActionMaxRaw:t.policy.perActionMaxRaw,remainingRaw:s});if(t.chainId!==84532||t.createStatus!=="confirmed"||t.chainState.status!=="active"||t.apiAccess!=="active")return a(t.revokeStatus==="confirmed"||t.chainState.status==="revoked"?"mandate_revoked":Date.parse(t.policy.expiresAt)<=Date.now()?"mandate_expired":"mandate_inactive");if(i===0n||!t.policy.budgets.some(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase()))return a("mandate_inactive");if(o!==null&&i>o)return a("over_per_action_max");if(!t.policy.recipients.some(u=>u.toLowerCase()===n.recipient.toLowerCase()))return a("wrong_recipient");if((await this.identity()).address.toLowerCase()!==t.agentAddress.toLowerCase())return a("mandate_inactive");if(!this.#g)throw new Error("Configure maxGasCostWei before preparing mandate actions.");return this.prepareExactAction({accountId:t.accountId,accountAddress:t.accountAddress,chainId:84532,action:"transfer",tokenAddress:n.tokenAddress,recipient:n.recipient,amount:n.amountRaw},{delegationId:t.id,permissionId:t.permissionId,executorAddress:t.grantExecutor,maxGasCostWei:this.#g})}async buildDelegatedAction(e){let t={recipient:e.recipient,amount:e.amount,...e.token?{token:e.token}:{}};return this.#t(n=>this.#e.post(`/v1/delegations/${e.delegationId}/actions`,t,n),!1)}async buildDelegatedSwap(e){let t={action:"swap",tokenIn:e.tokenIn,tokenOut:e.tokenOut,amountIn:e.amountIn,...e.maxSlippageBps!=null?{maxSlippageBps:e.maxSlippageBps}:{}};return this.#t(n=>this.#e.post(`/v1/delegations/${e.delegationId}/actions`,t,n),!1)}async raiseRequest(e,t){return this.#t(n=>this.#e.post("/v1/agents/requests",{calls:e,reason:t},n),!1)}async spendUnderGrant(e,t,n,i,o){C(e),p(t,32),p(n,32);let s=await this.#l(t),a=he(["bytes1","bytes32","bytes"],["0x00",n,s]);try{return await this.#e.post("/v1/userops",{userOp:{...e,signature:a},userOpHash:t,intentType:o},i)}catch(c){throw c instanceof h&&(c.userOpHash=t),c}}async getUserOp(e){return w(e),this.#t(t=>this.#e.get(`/v1/userops/${e}`,t))}async getUserOpByHash(e,t){return(!Number.isSafeInteger(e)||e<1)&&d(),p(t,32),j(await this.#t(n=>this.#e.get(`/v1/userops/by-hash/${e}/${t}`,n)),t)}async awaitTerminal(e,t){return X(n=>this.getUserOp(n),e,t,()=>Date.now(),n=>new Promise(i=>setTimeout(i,n)))}async listApis(e){return this.#t(t=>this.#e.get(`/v1/apis/providers?chainId=${e}`,t))}async getApiProvider(e,t){let n=t!==void 0?`?chainId=${t}`:"";return this.#t(i=>this.#e.get(`/v1/apis/providers/${e}${n}`,i))}async callApi(e){let t=await this.#t(n=>this.#e.post("/v1/apis/calls/requirements",e,n),!1);return w(t?.requirementId),["ready","pending_approval"].includes(t.status)||d(),this.#m(t)}async resumeApi(e,t=fe){w(e);let n=z(await this.#t(i=>this.#e.get(`/v1/apis/calls/relay/${e}`,i)),e);return n.status==="ready"?this.#m({requirementId:e,status:"ready",...T(n.ready)},t):n.status!=="pending_approval"?n:this.#m({requirementId:e,status:"pending_approval",approvalId:e,expiresAt:""},t)}async#m(e,t=fe){let n;if(e.status==="pending_approval"){let a=await X(c=>this.#t(u=>this.#e.get(`/v1/apis/calls/requirements/${c}`,u)),e.requirementId,t,()=>Date.now(),c=>new Promise(u=>setTimeout(u,c)));if(a.status==="approved"&&a.payload&&a.authorization&&a.index!==void 0&&a.validator)n={payload:a.payload,authorization:a.authorization,index:a.index,validator:a.validator};else{let c=a.status==="declined"?"owner_declined":a.status==="expired"?"approval_timed_out":"approval_still_pending",u=c==="owner_declined"?"The owner declined this call. Nothing was signed and nothing was charged.":c==="approval_timed_out"?"The owner did not respond before the approval window closed. Nothing was signed and nothing was charged.":"The owner has not answered yet. Nothing was signed or charged. Resume this requirement or ask the owner to check their pending approvals.";return["pending","declined","expired"].includes(a.status)||d(),{status:a.status==="pending"?"pending_approval":"declined",declineReason:c,message:u,requirementId:e.requirementId,approvalId:e.status==="pending_approval"?e.approvalId:void 0}}}else n=e;n=T(n);let i=await this.identity(),o=await this.#l(n.payload),s=he(["bytes1","address","bytes"],["0x01",n.validator,Fe([{type:"address"},{type:"tuple",components:[{name:"from",type:"address"},{name:"to",type:"address"},{name:"value",type:"uint256"},{name:"validAfter",type:"uint256"},{name:"validBefore",type:"uint256"},{name:"nonce",type:"bytes32"}]},{type:"uint32"},{type:"bytes"}],[i.address,{from:n.authorization.from,to:n.authorization.to,value:BigInt(n.authorization.value),validAfter:BigInt(n.authorization.validAfter),validBefore:BigInt(n.authorization.validBefore),nonce:n.authorization.nonce},n.index,o])]);try{return z(await this.#t(a=>this.#e.post("/v1/apis/calls/relay",{requirementId:e.requirementId,signature:s},a),!1),e.requirementId)}catch(a){throw a instanceof h&&(a.requirementId=e.requirementId),a}}async getApiBudget(e){return this.#t(t=>this.#e.get(`/v1/apis/budget?chainId=${e}`,t))}reset(){this.#n+=1,this.#u=null,this.#p=null,this.#i=null,this.#c=null,this.#s=null}async#h(){if(!this.#c){let e=await this.identity();this.#c??=new O(e.fingerprint)}return this.#c}async#t(e,t=!0){let n=await this.ensureSession();try{return await e(n)}catch(i){if(t&&i instanceof h&&i.status===401){let o=this.#s&&this.#s.token!==n?await this.ensureSession():await this.mintSession();return await e(o)}throw i}}};function We(r,e={}){let t={address:r.address,publicKey:r.publicKey,fingerprint:V(r.address),backend:e.backend??"injected",createdAt:e.createdAt??new Date().toISOString()};return{ensure:async()=>({...t}),sign:n=>r.signMessage({message:n}),signHash:n=>r.sign({hash:n})}}import{recoverMessageAddress as Ve}from"viem";async function Ke(r,e,t){if(typeof t!="string"||t.trim().length<1||t.trim().length>80)throw new Error("Agent name must contain 1 to 80 characters.");let n;try{n=H(await r.ensure())}catch{throw Q()}let i=await e.app.context();w(i.appId),w(i.keyId),["test","live"].includes(i.environment)||d();let o=n.publicKey.toLowerCase(),s=n.address.toLowerCase(),a=await e.agents.challenge({publicKey:o,name:t.trim()});(!a||a.appId!==i.appId||a.environment!==i.environment||a.publicKey!==o||a.address!==s||!/^dach_[A-Za-z0-9_-]{43}$/.test(a.id)||typeof a.expiresAt!="string"||!Number.isFinite(Date.parse(a.expiresAt))||Date.parse(a.expiresAt)<=Date.now()||Date.parse(a.expiresAt)>Date.now()+36e4)&&d();let c=["Botanary agent registration","Purpose: register-agent-key","Audience: botanary:developer-platform:2026-09-07",`Application: ${i.appId}`,`Environment: ${i.environment}`,`API key: ${i.keyId}`,`Public key: ${o}`,`Address: ${s}`,`Nonce: ${a.id}`,`Expires: ${a.expiresAt}`,"This proof does not authorize an account connection or an on-chain grant."].join(`
1
+ var h=class extends Error{constructor(t,n,i=null,o=null){super(t);this.status=n;this.details=i;this.retryAfterSeconds=o;this.name="BotanaryApiError"}requestId;code;operationId;userOpHash;requirementId;ambiguous=!1};import{encodeFunctionData as E,encodePacked as ve,decodeFunctionData as F,parseAbi as U,getAddress as I,zeroAddress as Pe,zeroHash as Re}from"viem";import{entryPoint07Address as ee,getUserOperationHash as Se}from"viem/account-abstraction";import{publicKeyToAddress as Ie}from"viem/accounts";function d(){let r=new h("Botanary returned an invalid response. Nothing was signed.",0);throw r.code="invalid_response",r}function y(r){return typeof r!="object"||r===null||Array.isArray(r)?d():r}function p(r,e){(typeof r!="string"||!/^0x(?:[0-9a-fA-F]{2})*$/.test(r)||e!==void 0&&r.length!==2+2*e)&&d()}function w(r){(typeof r!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(r)||/^(?:bsk|ags|dcs|dca|dcr)_/.test(r))&&d()}function L(r){(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r)||BigInt(r)>=2n**256n)&&d()}function H(r){let e=y(r);return p(e.address,20),p(e.publicKey,65),(!e.publicKey.startsWith("0x04")||Ie(e.publicKey).toLowerCase()!==e.address.toLowerCase()||typeof e.fingerprint!="string"||!/^[A-Za-z0-9]{1,32}$/.test(e.fingerprint)||typeof e.backend!="string"||!/^[A-Za-z0-9_-]{1,64}$/.test(e.backend)||typeof e.createdAt!="string"||!Number.isFinite(Date.parse(e.createdAt)))&&d(),Object.freeze({address:e.address,publicKey:e.publicKey,fingerprint:e.fingerprint,backend:e.backend,createdAt:e.createdAt})}function x(r,e=256){if(typeof r!="string"||!/^(0|[1-9][0-9]{0,77}|0x[0-9a-fA-F]{1,64})$/.test(r))return d();let t=BigInt(r);return t>=2n**BigInt(e)?d():t}function C(r){let e=y(r);for(let t of["sender","entryPoint"])p(e[t],20);for(let t of["nonce","callGasLimit","verificationGasLimit","preVerificationGas","maxFeePerGas","maxPriorityFeePerGas"])x(e[t]);for(let t of["callData","signature"])p(e[t]);for(let t of["paymasterVerificationGasLimit","paymasterPostOpGasLimit"])e[t]!=null&&x(e[t],128);(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d();for(let t of["factory","paymaster"])e[t]!=null&&p(e[t],20);for(let t of["factoryData","paymasterData"])e[t]!=null&&p(e[t]);return r}function T(r){let e=y(r);p(e.payload,32),p(e.validator,20),(!Number.isSafeInteger(e.index)||Number(e.index)<0||Number(e.index)>=2**32)&&d();let t=y(e.authorization);return p(t.from,20),p(t.to,20),p(t.nonce,32),L(t.value),L(t.validAfter),L(t.validBefore),BigInt(t.validBefore)<=BigInt(t.validAfter)&&d(),{payload:e.payload,validator:e.validator,index:Number(e.index),authorization:{from:t.from,to:t.to,nonce:t.nonce,value:t.value,validAfter:t.validAfter,validBefore:t.validBefore}}}function z(r,e){let t=y(r);(t.requirementId!==e||typeof t.simulated!="boolean")&&d();let n={ready:["not_submitted"],pending_approval:["not_submitted"],submitted:["pending"],settled:["settled"],declined:["not_submitted","not_settled"]};return(typeof t.status!="string"||!Object.hasOwn(n,t.status)||!n[t.status].includes(String(t.paymentStatus)))&&d(),t.providerStatus!==void 0&&(!Number.isSafeInteger(t.providerStatus)||Number(t.providerStatus)<100||Number(t.providerStatus)>599)&&d(),t.txHash!==void 0&&p(t.txHash,32),t.status==="settled"&&p(t.txHash,32),t.status==="ready"?T(t.ready):t.ready!==void 0&&d(),t}var te=U(["function execute(bytes32 mode, bytes executionCalldata) payable"]),M=U(["function executeUnderGrant(bytes32 permissionId, address target, uint256 value, bytes callData)","function executeUnderGrantWithAllowance(bytes32 permissionId, address target, uint256 value, address token, uint256 allowance, bytes callData)"]),_e=U(["function transfer(address to, uint256 amount) returns (bool)"]),ne=U(["function swapExactIn(address tokenIn, address tokenOut, uint256 amountIn, uint256 minOut, address to) returns (uint256 amountOut)"]),Ee=BigInt("0x000100000000008bdaba73cd9815d79069c247eb4bda0000");function f(r,e){return typeof r=="string"&&r.toLowerCase()===e.toLowerCase()}function b(r,e=!0){if(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r))return d();let t=BigInt(r);return t>=2n**256n||e&&t===0n?d():t}function re(r){let e=y(r),t=["accountId","accountAddress","chainId","action","tokenAddress","amount","recipient","tokenOutAddress","maxSlippageBps"];return Object.keys(e).some(n=>!t.includes(n))&&d(),w(e.accountId),p(e.accountAddress,20),p(e.tokenAddress,20),b(e.amount),(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d(),e.action==="transfer"?(p(e.recipient,20),(e.tokenOutAddress!==void 0||e.maxSlippageBps!==void 0)&&d()):e.action==="swap"?(p(e.tokenOutAddress,20),(e.recipient!==void 0||e.maxSlippageBps!==void 0&&(!Number.isInteger(e.maxSlippageBps)||Number(e.maxSlippageBps)<1||Number(e.maxSlippageBps)>1e3))&&d()):d(),Object.freeze({...r,accountAddress:I(r.accountAddress),tokenAddress:I(r.tokenAddress),...r.recipient!==void 0?{recipient:I(r.recipient)}:{},...r.tokenOutAddress!==void 0?{tokenOutAddress:I(r.tokenOutAddress)}:{}})}function ie(r,e){if(w(r.delegationId),p(r.permissionId,32),p(r.executorAddress,20),b(r.maxGasCostWei),e.action==="swap"){let t=r;p(t.routerAddress,20),b(t.minAmountOut),t.verifyRoute!==void 0&&typeof t.verifyRoute!="function"&&d()}return Object.freeze({...r,executorAddress:I(r.executorAddress),...e.action==="swap"?{routerAddress:I(r.routerAddress)}:{}})}async function se(r,e,t){try{let n=y(r),i=y(n.execution),o=C(n.userOp),s=Object.freeze({...o,sender:I(o.sender),entryPoint:I(o.entryPoint)});p(n.userOpHash,32),(n.intentType!=="delegated_action"||y(n.simulation).willSucceed!==!0||i.accountId!==e.accountId||!f(i.accountAddress,e.accountAddress)||i.chainId!==e.chainId||i.action!==e.action||i.amount!==e.amount||!f(i.tokenAddress,e.tokenAddress)||i.delegationId!==t.delegationId||!f(i.permissionId,t.permissionId)||i.gasMethod!=="native"||!f(s.sender,e.accountAddress)||s.chainId!==e.chainId||!f(s.entryPoint,ee)||s.factory!=null||s.factoryData!=null||s.paymaster!=null||s.paymasterData!=null||s.paymasterVerificationGasLimit!=null||s.paymasterPostOpGasLimit!=null)&&d();let a=x(s.nonce);a>>64n!==Ee&&d();let c=x(s.callGasLimit,128),u=x(s.verificationGasLimit,128),l=x(s.preVerificationGas),k=x(s.maxFeePerGas,128),m=x(s.maxPriorityFeePerGas,128);(m>k||(c+u+l)*k>b(t.maxGasCostWei))&&d();let g=s.callData,v;if(e.action==="transfer"){(!f(i.recipient,e.recipient)||i.tokenOutAddress!==void 0||i.maxSlippageBps!==void 0)&&d();let A=f(e.tokenAddress,Pe);v=E({abi:M,functionName:"executeUnderGrant",args:[t.permissionId,A?e.recipient:e.tokenAddress,A?b(e.amount):0n,A?"0x":E({abi:_e,functionName:"transfer",args:[e.recipient,b(e.amount)]})]})}else{let A=t;(!f(i.tokenOutAddress,e.tokenOutAddress)||i.recipient!==void 0||i.maxSlippageBps!==(e.maxSlippageBps??50))&&d();let q=F({abi:te,data:g}).args[1],P=F({abi:M,data:`0x${q.slice(106)}`});P.functionName!=="executeUnderGrantWithAllowance"&&d();let S=P.args[5],_=Object.freeze({chainId:e.chainId,accountAddress:e.accountAddress,routerAddress:A.routerAddress,tokenAddress:e.tokenAddress,tokenOutAddress:e.tokenOutAddress,amount:e.amount,minAmountOut:A.minAmountOut,callData:S});if(A.verifyRoute)await A.verifyRoute(_)!==!0&&d();else{let J=F({abi:ne,data:S}),[Ae,we,xe,be,ke]=J.args;(!f(Ae,e.tokenAddress)||!f(we,e.tokenOutAddress)||xe!==b(e.amount)||be<b(A.minAmountOut)||!f(ke,e.accountAddress)||!f(E({abi:ne,functionName:"swapExactIn",args:J.args}),S))&&d()}v=E({abi:M,functionName:"executeUnderGrantWithAllowance",args:[t.permissionId,A.routerAddress,0n,e.tokenAddress,b(e.amount),S]})}let R=E({abi:te,functionName:"execute",args:[Re,ve(["address","uint256","bytes"],[t.executorAddress,0n,v])]});f(g,R)||d();let $=Se({entryPointAddress:ee,entryPointVersion:"0.7",chainId:e.chainId,userOperation:{sender:s.sender,nonce:a,callData:s.callData,callGasLimit:c,verificationGasLimit:u,preVerificationGas:l,maxFeePerGas:k,maxPriorityFeePerGas:m,signature:"0x"}});f($,n.userOpHash)||d();let G=Object.freeze({...e,delegationId:t.delegationId,permissionId:t.permissionId,gasMethod:"native",...e.action==="swap"?{maxSlippageBps:e.maxSlippageBps??50}:{}});return{userOp:Object.freeze({sender:s.sender,chainId:s.chainId,entryPoint:s.entryPoint,nonce:s.nonce,callData:s.callData,callGasLimit:s.callGasLimit,verificationGasLimit:s.verificationGasLimit,preVerificationGas:s.preVerificationGas,maxFeePerGas:s.maxFeePerGas,maxPriorityFeePerGas:s.maxPriorityFeePerGas,signature:"0x"}),userOpHash:$,execution:G}}catch{return d()}}function j(r,e){try{let t=y(r);if(w(t.id),!f(t.userOpHash,e)||!["pending","included","failed"].includes(String(t.status)))throw new Error;return t.txHash!=null&&p(t.txHash,32),t}catch{let t=new h("Botanary returned an invalid receipt. Reconcile using the original operation hash.",0);throw t.code="invalid_response",t.userOpHash=e,t.ambiguous=!0,t}}import{keccak256 as Le,toHex as ze,encodePacked as he,encodeAbiParameters as Fe}from"viem";import{randomBytes as Oe}from"node:crypto";import{hexToBytes as Be}from"viem";var ae="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function W(r){let e=0,t=0,n="";for(let i of r)for(e=e<<8|i,t+=8;t>=5;)n+=ae[e>>>t-5&31],t-=5;return t>0&&(n+=ae[e<<5-t&31]),n}var oe=3,de=5;function V(r){let e=Be(r);return W(e.slice(0,oe))}function K(){return W(Oe(de))}var ce=600*1e3,O=class{#r;#e;#d;#a;#i=null;constructor(e,t){this.#r=e,this.#e=t?.ttlMs??ce,this.#d=t?.now??Date.now,this.#a=t?.nonce??K}current(){let e=this.#d();return(!this.#i||this.#i.expiresAt<=e)&&this.#c(e),this.#s()}regenerate(){return this.#c(this.#d()),this.#s()}#c(e){this.#i={nonce:this.#a(),issuedAt:e,expiresAt:e+this.#e}}#s(){let e=this.#i;if(!e)throw new Error("unreachable: no pairing code minted");return{code:`${this.#r}-${e.nonce}`,fingerprint:this.#r,nonce:e.nonce,issuedAt:new Date(e.issuedAt).toISOString(),expiresAt:new Date(e.expiresAt).toISOString()}}};import Ne from"openapi-fetch";var ue=[{method:"post",path:"/agents/pair"},{method:"post",path:"/agents/session/nonce"},{method:"post",path:"/agents/session"},{method:"get",path:"/agents/me"},{method:"get",path:"/agents/requests"},{method:"post",path:"/agents/requests"},{method:"get",path:"/balance"},{method:"get",path:"/chains"},{method:"get",path:"/gas/methods"},{method:"get",path:"/account"},{method:"post",path:"/delegations/{delegationId}/actions"},{method:"post",path:"/delegations/{delegationId}/actions/exact"},{method:"post",path:"/userops"},{method:"get",path:"/userops/{userOpId}"},{method:"get",path:"/userops/by-hash/{chainId}/{userOpHash}"},{method:"get",path:"/apis/providers"},{method:"get",path:"/apis/providers/{providerId}"},{method:"get",path:"/apis/budget"},{method:"post",path:"/apis/calls/requirements"},{method:"get",path:"/apis/calls/requirements/{id}"},{method:"post",path:"/apis/calls/relay"},{method:"get",path:"/apis/calls/relay/{id}"}];var pe="https://botanary-canonical-path.invalid",le="(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})",ut=`${le}+`,He=new RegExp(`^${le}+$`),Ce=new RegExp("^(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*$"),Te=/^\.{1,2}$/,Ue=/[/\\#?]/,De=/[\u0000-\u001f\u007f]/,$e=6;function Ge(r){let e=r;for(let t=0;t<$e;t+=1){if(Te.test(e)||Ue.test(e)||De.test(e))return!0;if(!e.includes("%"))return!1;let n;try{n=decodeURIComponent(e)}catch{return!0}if(n===e)return!1;e=n}return!0}function ge(r){if(typeof r!="string"||r.length===0||!r.startsWith("/")||r.startsWith("//"))return null;let e=r.indexOf("?"),t=e===-1?r:r.slice(0,e),n=e===-1?"":r.slice(e),i=t.split("/");for(let s=1;s<i.length;s+=1){let a=i[s];if(!He.test(a)||Ge(a))return null}if(n!==""&&!Ce.test(n.slice(1)))return null;let o;try{o=new URL(t+n,pe)}catch{return null}return o.origin!==pe||o.hash!==""||o.pathname!==t||o.search!==n?null:{path:t,query:n,full:t+n}}var D=class{#r;constructor(e,t=fetch){let n=new URL(e);if(n.username||n.password||n.search||n.hash||n.pathname!=="/"||n.protocol!=="https:"&&!(n.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(n.hostname)))throw new Error("Agent API origin must be HTTPS, or HTTP on loopback.");this.#r=Ne({baseUrl:n.origin+"/v1",fetch:qe(t)})}get(e,t){return this.#e("get",e,void 0,t)}post(e,t,n){return this.#e("post",e,t,n)}async#e(e,t,n,i){let o=ge(t);if(!o?.path.startsWith("/v1/"))throw new Error("Invalid agent API path.");if(i!==void 0&&!/^ags_[a-f0-9]{64}$/.test(i))throw new Error("Expected an agent session credential.");let s=o.path.slice(3),a=ue.find(m=>m.method===e&&new RegExp("^"+m.path.replace(/\{[^}]+\}/g,"[A-Za-z0-9_-]+")+"$").test(s));if(!a)throw new Error("This route is outside the reviewed agent API.");let c={};a.path.split("/").forEach((m,g)=>{m.startsWith("{")&&(c[m.slice(1,-1)]=s.split("/")[g])});let u=Object.fromEntries(new URLSearchParams(o.query)),l=new AbortController,k=setTimeout(()=>l.abort(),3e4);try{let m=await this.#r.request(e,a.path,{params:{path:c,query:u},...n===void 0?{}:{body:n},headers:i?{authorization:`Bearer ${i}`}:{},credentials:"omit",redirect:"error",cache:"no-store",signal:l.signal,parseAs:"text"}),g=m.response,v=m.error;if(g.ok)try{v=m.data?JSON.parse(m.data):{}}catch{throw new Error("Invalid JSON response.")}if(!g.ok){let R=Z(Z(v)?.error),G=(typeof R?.message=="string"?R.message:`Agent request failed with ${g.status}.`).replace(/(?:bsk_(?:test|live)|ags|dcs|dca|dcr)_[A-Za-z0-9_-]+/g,"[redacted]").replace(/0x[a-fA-F0-9]{64,}/g,"[redacted]"),B=g.headers.get("retry-after"),A=B&&/^\d+(?:\.\d+)?$/.test(B)?Number(B):null,N=Z(R?.details),q=N&&Object.fromEntries(Object.entries(N).filter(([S,_])=>["missingModule","declineReason","chainId"].includes(S)&&(typeof _=="number"||typeof _=="string"&&/^[a-zA-Z0-9_-]{1,100}$/.test(_)))),P=new h(G.slice(0,1e3),g.status,q,A);throw P.requestId=me(g.headers.get("x-request-id")),P.code=me(R?.code)??"http_error",P.ambiguous=e==="post"&&g.status>=500,P}return v}catch(m){if(m instanceof h)throw m;let g=new h("The agent request did not complete. Check the existing operation before retrying.",0);throw g.code=l.signal.aborted?"timeout":"transport_error",g.ambiguous=e==="post",g}finally{clearTimeout(k)}}};function Z(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)?r:null}function me(r){return typeof r=="string"&&/^[a-zA-Z0-9_-]{1,128}$/.test(r)&&!/^(?:ags|bsk|dcs|dca|dcr)_/.test(r)?r:void 0}function qe(r){return async(e,t)=>{let n=e instanceof Request?e:new Request(e,t),i=n.signal,o=()=>{},s=new Promise((c,u)=>{o=()=>u(new Error("Request aborted.")),i.aborted?o():i.addEventListener("abort",o,{once:!0})}),a;try{let c=await Promise.race([r(n),s]);if(!c.body)return c;a=c.body.getReader();let u=[],l=0;for(;;){let g=await Promise.race([a.read(),s]);if(g.done)break;if(l+=g.value.byteLength,l>4*1024*1024)throw new Error("Response too large.");u.push(g.value)}let k=new Uint8Array(l),m=0;for(let g of u)k.set(g,m),m+=g.byteLength;return new Response(k,{status:c.status,statusText:c.statusText,headers:c.headers})}finally{i.removeEventListener("abort",o),a&&a.cancel().catch(()=>{})}}}import{recoverAddress as Me}from"viem";async function X(r,e,t,n,i){if(!Number.isFinite(t)||t<0||t>36e5)throw new Error("Invalid wait budget.");let o=n(),s=await r(e);for(;["pending","pending_approval","submitted","queued"].includes(String(s.status))&&n()-o<t;)await i(1500),s=await r(e);return s}var je=3e4,fe=300*1e3,ye="https://api.app.botanary.xyz",Y=class{#r;#e;#d;#a;#i=null;#c=null;#s=null;#u=null;#p=null;#n=0;#g;constructor(e,t={}){if(this.#r=e,this.#d=t.baseUrl??ye,this.#e=t.transport??new D(this.#d,t.fetch),this.#a=t.provenance??{},t.maxGasCostWei!==void 0&&(!/^[1-9][0-9]{0,77}$/.test(t.maxGasCostWei)||BigInt(t.maxGasCostWei)>=2n**256n))throw new Error("Use a positive integer agent gas ceiling in wei.");this.#g=t.maxGasCostWei??null}get apiBaseUrl(){return this.#d}get identityMaterialized(){return this.#i!==null||this.#u!==null}setProvenance(e){this.#a=e}replaceSigner(e){if(this.identityMaterialized)throw new Error("Cannot replace a materialized agent signer.");this.#r=e,this.reset()}async identity(){if(this.#i)return this.#i;if(this.#u)return this.#u;let e=this.#n,t=(async()=>{let n;try{n=await this.#r.ensure()}catch{throw this.#o()}if(e!==this.#n)throw this.#o();let i=H(n);return this.#i=i,i})();this.#u=t;try{return await t}finally{this.#u===t&&(this.#u=null)}}#o(){let e=new h("The agent signer could not complete the request.",0);return e.code="signer_error",e}async#l(e){p(e,32);let t=this.#n,n=await this.identity();if(t!==this.#n)throw this.#o();try{let i=await this.#r.signHash(e);p(i,65);let o=await Me({hash:e,signature:i});if(t!==this.#n||o.toLowerCase()!==n.address.toLowerCase())throw this.#o();return i}catch{throw this.#o()}}async pairingCode(){return(await this.#h()).current()}async regeneratePairingCode(){return(await this.#h()).regenerate()}async pair(){let e=await this.pairingCode(),t=Math.floor(Date.now()/1e3),n=await this.identity(),i=`${e.code}|${t}`,o=Le(ze(i));return await this.#e.post("/v1/agents/pair",{code:e.code,publicKey:n.publicKey,timestamp:t,signature:await this.#l(o),...this.#a.client?{client:this.#a.client}:{},...this.#a.profile?{profile:this.#a.profile}:{}}),e}async mintSession(){if(this.#p)return this.#p;let e=this.#n,t=(async()=>{let n=await this.identity(),i=y(await this.#e.post("/v1/agents/session/nonce",{address:n.address}));if(p(i.nonce,32),e!==this.#n)throw this.#o();let o=await this.#l(i.nonce),s=y(await this.#e.post("/v1/agents/session",{address:n.address,nonce:i.nonce,signature:o}));if((typeof s.token!="string"||!/^ags_[a-f0-9]{64}$/.test(s.token)||typeof s.expiresAt!="string"||!Number.isFinite(Date.parse(s.expiresAt))||Date.parse(s.expiresAt)<=Date.now()||Date.parse(s.expiresAt)>Date.now()+366e4)&&d(),e!==this.#n)throw this.#o();return this.#s={token:s.token,safeUntil:Date.parse(s.expiresAt)-je},s.token})();this.#p=t;try{return await t}finally{this.#p===t&&(this.#p=null)}}async ensureSession(){return this.#s&&this.#s.safeUntil>Date.now()?this.#s.token:this.mintSession()}async me(){return this.#t(e=>this.#e.get("/v1/agents/me",e))}async getBalance(){return this.#t(e=>this.#e.get("/v1/balance",e))}async listRequests(){return this.#t(e=>this.#e.get("/v1/agents/requests",e))}async listChains(){return this.#t(e=>this.#e.get("/v1/chains",e))}async getGasMethods(e){let t=new URLSearchParams;e.chainId!==void 0&&t.set("chainId",String(e.chainId)),e.accountId&&t.set("accountId",e.accountId),e.key&&t.set("key",e.key);let n=t.toString();return this.#t(i=>this.#e.get(`/v1/gas/methods${n?`?${n}`:""}`,i))}async getAccount(e){let t=new URLSearchParams;e.chainId!==void 0&&t.set("chainId",String(e.chainId));let n=t.toString();return this.#t(i=>this.#e.get(`/v1/account${n?`?${n}`:""}`,i))}async prepareExactAction(e,t){let n=re(e),i=ie(t,n),o=this.#n,s=await this.#t(u=>this.#e.post(`/v1/delegations/${i.delegationId}/actions/exact`,n,u),!1),a=await se(s,n,i),c;return Object.freeze({status:"prepared",userOpHash:a.userOpHash,execution:a.execution,submit:()=>(c||(c=(async()=>{if(o!==this.#n)throw this.#o();let u=await this.ensureSession();if(o!==this.#n)throw this.#o();return j(await this.spendUnderGrant(a.userOp,a.userOpHash,i.permissionId,u,"delegated_action"),a.userOpHash)})()),c),reconcile:()=>this.getUserOpByHash(n.chainId,a.userOpHash)})}async prepareAction(e){let{mandate:t,action:n}=structuredClone(e),i=/^[1-9][0-9]{0,77}$/.test(n.amountRaw)?BigInt(n.amountRaw):0n,o=t.policy.perActionMaxRaw&&/^[1-9][0-9]{0,77}$/.test(t.policy.perActionMaxRaw)?BigInt(t.policy.perActionMaxRaw):null,s=t.chainState.remaining?.find(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase())?.remainingRaw??null,a=u=>Object.freeze({status:"refused",reason:u,transactionSubmitted:!1,requestedAmountRaw:n.amountRaw,perActionMaxRaw:t.policy.perActionMaxRaw,remainingRaw:s});if(t.chainId!==84532||!["submitted","unresolved","confirmed"].includes(t.createStatus)||t.chainState.status!=="active"||t.apiAccess!=="active")return a(t.revokeStatus==="confirmed"||t.chainState.status==="revoked"?"mandate_revoked":Date.parse(t.policy.expiresAt)<=Date.now()?"mandate_expired":"mandate_inactive");if(i===0n||!t.policy.budgets.some(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase()))return a("mandate_inactive");if(o!==null&&i>o)return a("over_per_action_max");if(!t.policy.recipients.some(u=>u.toLowerCase()===n.recipient.toLowerCase()))return a("wrong_recipient");if((await this.identity()).address.toLowerCase()!==t.agentAddress.toLowerCase())return a("mandate_inactive");if(!this.#g)throw new Error("Configure maxGasCostWei before preparing mandate actions.");return this.prepareExactAction({accountId:t.accountId,accountAddress:t.accountAddress,chainId:84532,action:"transfer",tokenAddress:n.tokenAddress,recipient:n.recipient,amount:n.amountRaw},{delegationId:t.id,permissionId:t.permissionId,executorAddress:t.grantExecutor,maxGasCostWei:this.#g})}async buildDelegatedAction(e){let t={recipient:e.recipient,amount:e.amount,...e.token?{token:e.token}:{}};return this.#t(n=>this.#e.post(`/v1/delegations/${e.delegationId}/actions`,t,n),!1)}async buildDelegatedSwap(e){let t={action:"swap",tokenIn:e.tokenIn,tokenOut:e.tokenOut,amountIn:e.amountIn,...e.maxSlippageBps!=null?{maxSlippageBps:e.maxSlippageBps}:{}};return this.#t(n=>this.#e.post(`/v1/delegations/${e.delegationId}/actions`,t,n),!1)}async raiseRequest(e,t){return this.#t(n=>this.#e.post("/v1/agents/requests",{calls:e,reason:t},n),!1)}async spendUnderGrant(e,t,n,i,o){C(e),p(t,32),p(n,32);let s=await this.#l(t),a=he(["bytes1","bytes32","bytes"],["0x00",n,s]);try{return await this.#e.post("/v1/userops",{userOp:{...e,signature:a},userOpHash:t,intentType:o},i)}catch(c){throw c instanceof h&&(c.userOpHash=t),c}}async getUserOp(e){return w(e),this.#t(t=>this.#e.get(`/v1/userops/${e}`,t))}async getUserOpByHash(e,t){return(!Number.isSafeInteger(e)||e<1)&&d(),p(t,32),j(await this.#t(n=>this.#e.get(`/v1/userops/by-hash/${e}/${t}`,n)),t)}async awaitTerminal(e,t){return X(n=>this.getUserOp(n),e,t,()=>Date.now(),n=>new Promise(i=>setTimeout(i,n)))}async listApis(e){return this.#t(t=>this.#e.get(`/v1/apis/providers?chainId=${e}`,t))}async getApiProvider(e,t){let n=t!==void 0?`?chainId=${t}`:"";return this.#t(i=>this.#e.get(`/v1/apis/providers/${e}${n}`,i))}async callApi(e){let t=await this.#t(n=>this.#e.post("/v1/apis/calls/requirements",e,n),!1);return w(t?.requirementId),["ready","pending_approval"].includes(t.status)||d(),this.#m(t)}async resumeApi(e,t=fe){w(e);let n=z(await this.#t(i=>this.#e.get(`/v1/apis/calls/relay/${e}`,i)),e);return n.status==="ready"?this.#m({requirementId:e,status:"ready",...T(n.ready)},t):n.status!=="pending_approval"?n:this.#m({requirementId:e,status:"pending_approval",approvalId:e,expiresAt:""},t)}async#m(e,t=fe){let n;if(e.status==="pending_approval"){let a=await X(c=>this.#t(u=>this.#e.get(`/v1/apis/calls/requirements/${c}`,u)),e.requirementId,t,()=>Date.now(),c=>new Promise(u=>setTimeout(u,c)));if(a.status==="approved"&&a.payload&&a.authorization&&a.index!==void 0&&a.validator)n={payload:a.payload,authorization:a.authorization,index:a.index,validator:a.validator};else{let c=a.status==="declined"?"owner_declined":a.status==="expired"?"approval_timed_out":"approval_still_pending",u=c==="owner_declined"?"The owner declined this call. Nothing was signed and nothing was charged.":c==="approval_timed_out"?"The owner did not respond before the approval window closed. Nothing was signed and nothing was charged.":"The owner has not answered yet. Nothing was signed or charged. Resume this requirement or ask the owner to check their pending approvals.";return["pending","declined","expired"].includes(a.status)||d(),{status:a.status==="pending"?"pending_approval":"declined",declineReason:c,message:u,requirementId:e.requirementId,approvalId:e.status==="pending_approval"?e.approvalId:void 0}}}else n=e;n=T(n);let i=await this.identity(),o=await this.#l(n.payload),s=he(["bytes1","address","bytes"],["0x01",n.validator,Fe([{type:"address"},{type:"tuple",components:[{name:"from",type:"address"},{name:"to",type:"address"},{name:"value",type:"uint256"},{name:"validAfter",type:"uint256"},{name:"validBefore",type:"uint256"},{name:"nonce",type:"bytes32"}]},{type:"uint32"},{type:"bytes"}],[i.address,{from:n.authorization.from,to:n.authorization.to,value:BigInt(n.authorization.value),validAfter:BigInt(n.authorization.validAfter),validBefore:BigInt(n.authorization.validBefore),nonce:n.authorization.nonce},n.index,o])]);try{return z(await this.#t(a=>this.#e.post("/v1/apis/calls/relay",{requirementId:e.requirementId,signature:s},a),!1),e.requirementId)}catch(a){throw a instanceof h&&(a.requirementId=e.requirementId),a}}async getApiBudget(e){return this.#t(t=>this.#e.get(`/v1/apis/budget?chainId=${e}`,t))}reset(){this.#n+=1,this.#u=null,this.#p=null,this.#i=null,this.#c=null,this.#s=null}async#h(){if(!this.#c){let e=await this.identity();this.#c??=new O(e.fingerprint)}return this.#c}async#t(e,t=!0){let n=await this.ensureSession();try{return await e(n)}catch(i){if(t&&i instanceof h&&i.status===401){let o=this.#s&&this.#s.token!==n?await this.ensureSession():await this.mintSession();return await e(o)}throw i}}};function We(r,e={}){let t={address:r.address,publicKey:r.publicKey,fingerprint:V(r.address),backend:e.backend??"injected",createdAt:e.createdAt??new Date().toISOString()};return{ensure:async()=>({...t}),sign:n=>r.signMessage({message:n}),signHash:n=>r.sign({hash:n})}}import{recoverMessageAddress as Ve}from"viem";async function Ke(r,e,t){if(typeof t!="string"||t.trim().length<1||t.trim().length>80)throw new Error("Agent name must contain 1 to 80 characters.");let n;try{n=H(await r.ensure())}catch{throw Q()}let i=await e.app.context();w(i.appId),w(i.keyId),["test","live"].includes(i.environment)||d();let o=n.publicKey.toLowerCase(),s=n.address.toLowerCase(),a=await e.agents.challenge({publicKey:o,name:t.trim()});(!a||a.appId!==i.appId||a.environment!==i.environment||a.publicKey!==o||a.address!==s||!/^dach_[A-Za-z0-9_-]{43}$/.test(a.id)||typeof a.expiresAt!="string"||!Number.isFinite(Date.parse(a.expiresAt))||Date.parse(a.expiresAt)<=Date.now()||Date.parse(a.expiresAt)>Date.now()+36e4)&&d();let c=["Botanary agent registration","Purpose: register-agent-key","Audience: botanary:developer-platform:2026-09-07",`Application: ${i.appId}`,`Environment: ${i.environment}`,`API key: ${i.keyId}`,`Public key: ${o}`,`Address: ${s}`,`Nonce: ${a.id}`,`Expires: ${a.expiresAt}`,"This proof does not authorize an account connection or an on-chain grant."].join(`
2
2
  `);a.message!==c&&d();let u;try{if(u=await r.sign(c),p(u,65),(await Ve({message:c,signature:u})).toLowerCase()!==s)throw Q()}catch{throw Q()}let l=await e.agents.complete({challengeId:a.id,signature:u});return(!l||l.appId!==i.appId||l.environment!==i.environment||l.publicKey!==o||l.address!==s||l.grantStatus!=="unknown"||!["awaiting_connection","active","disabled","app_disabled","key_revoked","connection_revoked","unavailable"].includes(l.apiAccess)||l.apiAccess==="active"&&l.connectionId===null||l.apiAccess==="awaiting_connection"&&l.connectionId!==null)&&d(),l.connectionId!==null&&w(l.connectionId),l}function Q(){let r=new h("The agent signer could not complete registration.",0);return r.code="signer_error",r}export{Y as AgentRuntime,h as BotanaryApiError,ye as DEFAULT_API_BASE_URL,ce as DEFAULT_PAIRING_CODE_TTL_MS,oe as FINGERPRINT_BYTES,de as NONCE_BYTES,O as PairingCodeManager,X as awaitTerminalWith,We as createViemAgentSigner,W as crockfordBase32,V as deriveFingerprint,K as generatePairingNonce,Ke as registerAgent};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botanary/agent",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.5",
4
4
  "description": "Botanary agent session and execution runtime with an injected signer",
5
5
  "type": "module",
6
6
  "license": "MIT",