mppx 0.14.0 → 0.14.1
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 +6 -0
- package/dist/cli/cli.js +1 -1
- package/dist/internal/version.d.ts +2 -2
- package/dist/internal/version.js +1 -1
- package/dist/tempo/session/server/CloseSupervisor.d.ts +13 -1
- package/dist/tempo/session/server/CloseSupervisor.js +23 -2
- package/dist/tempo/session/server/index.d.ts +3 -1
- package/dist/tempo/session/server/index.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# mppx
|
|
2
2
|
|
|
3
|
+
## 0.14.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 908af47: `superviseCloses` accepts a `settlementSchedule`, in raw token units, and settles open channels whose spend crossed it, within spend, reporting them `settled`. Servers that charge with `charge()` rather than through the session handler, whose schedule runs only on its own charges, now collect spend without waiting for a close. `resolveSettlementSchedule` and `ResolvedSettlementSchedule` are exported.
|
|
8
|
+
|
|
3
9
|
## 0.14.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/dist/cli/cli.js
CHANGED
|
@@ -242,7 +242,7 @@ $&`).replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g,`$1$2`).replace(/\
|
|
|
242
242
|
`)){let e=t.trim();if(e===`[[keys]]`){r?.wallet_address&&n.push(r),r={wallet_type:`local`,wallet_address:``,chain_id:0};continue}if(!r)continue;let i=e.match(/^(\w+)\s*=\s*"?([^"]*)"?$/);if(!i)continue;let[,a,o]=i;a===`wallet_type`?r.wallet_type=o:a===`wallet_address`?r.wallet_address=o:a===`chain_id`&&(r.chain_id=Number.parseInt(o,10))}return r?.wallet_address&&n.push(r),n}catch{return[]}}function DP(e){let t=EP();if(t.length===0)return;let n=e.slice(6);if(n==="default"||n===``)return t[0];let r=Number.parseInt(n,10);if(!Number.isNaN(r)&&r>=0&&r<t.length)return t[r]}function OP(){if(IP!==void 0)return IP;try{m.execFileSync(`which`,[`tempo`],{stdio:`ignore`}),IP=!0}catch{IP=!1}return IP}async function kP(e){return new Promise((t,n)=>{m.execFile(`tempo`,[`mpp`,`sign`,`--challenge`,e],(e,r,i)=>{if(e){let t=i?.trim()||e.message;n(Error(`tempo mpp sign failed: ${t}`));return}let a=r.trim();if(!a){n(Error(`tempo mpp sign returned empty output`));return}t(a)})})}function AP(){let e=rk();if(NO(e.get())&&!OP()&&b.platform()===`darwin`)try{let t=m.execFileSync(`security`,[`dump-keychain`],{encoding:`utf-8`}),n=[];for(let e of t.split(`keychain:`)){let t=e.match(/"svce"<blob>="([^"]*)"/),r=e.match(/"acct"<blob>="([^"]*)"/);t?.[1]===jP&&r?.[1]&&n.push(r[1])}if(n.length>0)return e.set(n[0]),n[0]}catch{}}var jP,MP,NP,PP,FP,IP,LP=I((()=>{Ox(),yT(),Fx(),rC(),rP(),GE(),Uj(),dk(),ZO(),uA(),jP=`mppx`,MP=p.union([p.boolean(),p.literal(`true`).transform(()=>!0),p.literal(`false`).transform(()=>!1)]),NP=p.object({allowCustomEscrow:p.optional(MP),autoSwap:p.optional(MP),channel:p.optional(p.coerce.string()),deposit:p.optional(p.union([p.string(),p.number()])),payWith:p.optional(p.string()),slippage:p.optional(p.coerce.number()),tokenIn:p.optional(p.string())}),PP=[`allowCustomEscrow`,`autoSwap`,`channel`,`deposit`,`payWith`,`slippage`,`tokenIn`],FP=[`payable`,`unknownBalance`,`insufficientBalance`,`unsupported`]}));OS(),dk(),uA();const RP=[...Object.entries(_k),...Object.entries(vk),...Object.entries(yk),...Object.entries(bk)];function zP(){return lA({method:`evm`,supports:BP,async setup({challenge:e,options:t}){let n=e.request,r=n.network?VP(Dk(n.network)):void 0,i=n.network&&n.asset?RP.find(([,e])=>Tk(e,n.asset,n.network)):void 0,a=n.extra?.name,o=n.extra?.version,s=n.asset&&n.network&&typeof a==`string`&&typeof o==`string`?hk({address:n.asset,decimals:i?.[1].decimals??6,network:n.network,transfer:{name:a,type:`eip3009`,version:o}}):void 0,c=await ck(t.account);return{explorerUrl:r?.blockExplorers?.default?.url,methods:[...cA({account:c,...s&&{currencies:[s]}})],tokenDecimals:i?.[1].decimals??6,tokenSymbol:i?.[0]??``}}})}function BP(e){if(!ES(e))return!1;let t=e.request,n=t.extra,r=n?.assetTransferMethod;return t.scheme===`exact`&&typeof t.asset==`string`&&typeof t.network==`string`&&typeof n?.name==`string`&&typeof n?.version==`string`&&(r===void 0||r===`eip3009`)}function VP(e){return Object.values(Be).find(t=>t.id===e)}_S(),JS(),LP();const HP=[sP(),bA(),dA(),zP()];async function UP(e,t,n){let r=n;for(let n of t??[]){let t=await n.preparePayment?.({challenge:e,...r===void 0?{}:{credentialContext:r}});t&&`credentialContext`in t&&(r=t.credentialContext)}return r}function WP(e,t){let n=t?.plugins?.find(t=>JP(t,e));if(n)return{plugin:n};let r=qP(t)?.filter(t=>t.name===e.method&&t.intent===e.intent),i=r?.find(t=>t.canHandleChallenge?.({challenge:e})??!0);if(i)return{method:i};if(r?.length)return{};let a=HP.find(t=>JP(t,e));return a?{plugin:a}:{}}function GP(e,t){let n=qP(t);if(n?.length){let r=NS(e,n,AS(n,t?.paymentPreferences).entries);if(r){let e=t?.plugins?.find(e=>JP(e,r.challenge));return{challenge:r.challenge,...e?{plugin:e}:{method:r.method}}}return}for(let n of e){let e=WP(n,t);if(e.plugin||e.method)return{challenge:n,...e}}}function KP(e){let t=qP(e);if(t?.length)return AS(t,e?.paymentPreferences).header}function qP(e){return Array.isArray(e?.methods)?e.methods.flat():void 0}function JP(e,t){return e.supports?e.supports(t):e.method===t.method}async function YP(e){let t=XP(e);if(!t)return;let n=await import(t);return{config:n.default??n,path:t}}function XP(e){if(e){let n=c.resolve(e);return t.existsSync(n)?n:void 0}let n=process.env.MPPX_CONFIG?.trim();if(n){let e=c.resolve(n);return t.existsSync(e)?e:void 0}}function ZP(e,t){function n(t){let n={...t.request};if(t.method===`tempo`&&t.intent===`session`){let e={...n.methodDetails};delete e.sessionSnapshot,Object.keys(e).length?n.methodDetails=e:delete n.methodDetails}return $x({...t,request:n,id:e.id,expires:e.expires})}if(n(t)!==n(e))throw Error(`Payment request changed on retry. Run the command again to approve the new payment.`)}LP(),_S(),iM(),hN(),TN(),_O();function QP(e){return e??globalThis.fetch.bind(globalThis)}function $P(e,t){let n=e.request.methodDetails?.chainId;if(n!==void 0&&n!==t.chainId)throw Error(`Close challenge changed the session chain.`);if(typeof e.request.recipient!=`string`||e.request.recipient.toLowerCase()!==t.descriptor.payee.toLowerCase())throw Error(`Close challenge changed the session payee.`);if(typeof e.request.currency!=`string`||e.request.currency.toLowerCase()!==t.descriptor.token.toLowerCase())throw Error(`Close challenge changed the session token.`);if(Zj(e,t.escrow).toLowerCase()!==t.escrow.toLowerCase())throw Error(`Close challenge changed the session escrow.`);let r=LD(e);if(r&&r.channelId.toLowerCase()!==t.channelId.toLowerCase())throw Error(`Close challenge changed the session channel.`)}async function eF(e){$P(e.challenge,e.channel);let t=QP(e.fetch),n,r=async(r,i)=>{n&&=(await e.onChallenge?.(n),void 0);let a=await t(r,i);if(a.status!==402)return a;let o=cS(a).find(FD);if(!o)throw Error(`Refreshed close response did not include tempo/session.`);return $P(o,e.channel),n=o,a},i=wN({...e.manager,bootstrap:!1,escrow:e.channel.escrow,fetch:r});pN(i).rehydrate(e);let a=await i.close();if(!a)throw Error(`Session close response did not include a payment receipt.`);return{manager:i,receipt:a}}_S(),iM(),_M(),_O(),Tj(),Zx();var tF=class extends Error{name=`SessionStateError`;code=`SESSION_STATE_INVALID`;file;constructor(e,t={}){super(e,t.cause===void 0?void 0:{cause:t.cause}),this.file=t.file}},nF=class extends Error{name=`SessionBusyError`;code=`SESSION_BUSY`;exitCode=75;scope;owner;constructor(e,t){super(`Session scope is busy in process ${t.pid} on ${t.hostname}.`),this.scope=e,this.owner=t}};const rF=J.object({channelId:qx(),cumulativeAmount:J.string(),deposit:J.string(),descriptor:J.object({payer:Kx(),payee:Kx(),operator:Kx(),token:Kx(),salt:qx(),authorizedSigner:Kx(),expiringNonceHash:qx()}),escrow:Kx(),chainId:J.number(),opened:J.boolean()}),iF=J.object({name:J.optional(J.string()),address:Kx()}),aF=J.object({method:J.literal(`tempo`),intent:J.literal(`session`),status:J.literal(`success`),timestamp:J.string(),reference:J.string(),challengeId:J.string(),channelId:qx(),acceptedCumulative:J.string(),spent:J.string(),units:J.optional(J.number()),txHash:J.optional(qx())}),oF=J.object({version:J.literal(1),method:J.literal(`tempo`),intent:J.literal(`session`),status:J.enum([`opening`,`open`,`closing`,`stale`]),channel:rF,account:iF,endpoint:J.string(),challenge:gS,receipt:J.optional(aF),spent:J.string(),units:J.number(),createdAt:J.string(),updatedAt:J.string()}),sF=J.object({version:J.literal(1),channelId:qx()}),cF=J.object({version:J.literal(1),scope:J.string(),hostname:J.string(),pid:J.number(),token:J.string(),createdAt:J.string()});function lF(e,t,n,r){let i=e.safeParse(t);if(!i.success)throw Z(n,r,i.error);return i.data}function uF(e){let t=bF(e);return[t.payer,t.payee,t.token,t.escrow,t.chainId].join(`:`)}function dF(e){return{payer:e.descriptor.payer,payee:e.descriptor.payee,token:e.descriptor.token,escrow:e.escrow,chainId:e.chainId}}function fF(e={}){let t=e.stateRoot??mF(),n={root:t,channels:c.join(t,`channels`),locks:c.join(t,`locks`),preferred:c.join(t,`preferred`)},r=e.hostname??b.hostname(),i=e.pid??process.pid,a=e.now??(()=>new Date),o=e.isProcessAlive??KF;async function s(){for(let e of[n.root,n.channels,n.locks,n.preferred])await C.mkdir(e,{recursive:!0,mode:448}),await C.chmod(e,448)}async function l(e){let t=vF(e),r=hF(n,t),i=await LF(r);if(i===void 0)return;let a=OF(i,r);if(a.channel.channelId.toLowerCase()!==t)throw Z(r,`Session filename does not match its channel ID.`);return kF(a)}async function u(){let e;try{e=await C.readdir(n.channels,{withFileTypes:!0})}catch(e){if(JF(e,`ENOENT`))return[];throw Z(n.channels,`Unable to list managed sessions.`,e)}let t=[];for(let n of e){if(!n.isFile()||n.name.includes(`.tmp-`)||!n.name.endsWith(`.json`))continue;let e=n.name.slice(0,-5);vF(e);let r=await l(e);r&&t.push(r)}return t.sort((e,t)=>e.createdAt.localeCompare(t.createdAt))}async function d(e){await s();let t=wF(e.channel),r=vF(t.channelId),i=hF(n,r),o=await LF(i),c=o===void 0?void 0:OF(o,i);c&&AF(c,e,i);let l=EF(e.challenge,i);MF(l,t,i);let u=e.receipt?DF(e.receipt,t.channelId,i):void 0,d=c?lM(c.channel):void 0,f=IF(t.cumulativeAmount,d?.cumulativeAmount??0n),p=IF(e.spent??0n,u?BigInt(u.spent):0n,c?BigInt(c.spent):0n);if(p>f)throw Z(i,`Session spend exceeds the locally authorized cumulative amount.`);let m=TF(cM({...t,cumulativeAmount:f}),i),h=FF(c?.updatedAt,a(),i),g=PF(c?.receipt,u),_=CF(e.account,i),v=OF({version:1,method:`tempo`,intent:`session`,status:e.status,channel:m,account:{..._.name===void 0?c?.account.name===void 0?{}:{name:c.account.name}:{name:_.name},address:_.address},endpoint:SF(e.endpoint,i),challenge:l,...g!==void 0&&{receipt:g},spent:p.toString(),units:Math.max(c?.units??0,e.units??0,u?.units??0),createdAt:c?.createdAt??h,updatedAt:h},i);return await RF(i,v),kF(v)}async function f(e){let t=_F(n,e),r=await LF(t);if(r===void 0)return;let{channelId:i}=lF(sF,r,t,`Preferred session is invalid.`),a=await l(i);if(!a)throw Z(t,`Preferred session ${i} does not exist.`);return NF(a.channel,e,t),i}async function p(e,t){let r=vF(t),i=await l(r),a=_F(n,e);if(!i)throw Z(a,`Session ${r} does not exist.`);NF(i.channel,e,a),await RF(a,{version:1,channelId:r})}async function m(e,t){let r=_F(n,e),i=t===void 0?void 0:vF(t),a=await LF(r);if(a===void 0)return;let o=lF(sF,a,r,`Preferred session is invalid.`).channelId;i&&o.toLowerCase()!==i||await WF(r,`Unable to clear preferred session.`)}async function h(e){let t=vF(e),r=await l(t);r&&(await m(dF(r.channel),t),await WF(hF(n,t),`Unable to remove session.`))}async function g(e){await s();let t=gF(n,e);for(let n=0;n<3;n++){let n={version:1,scope:e,hostname:r,pid:i,token:_(),createdAt:a().toISOString()};try{if(await zF(t,n),await HF(VF(t))){await UF(t,n);continue}return{async release(){await UF(t,n)}}}catch(e){if(!JF(e,`EEXIST`))throw e}let s=await LF(t);if(s===void 0)continue;let c=lF(cF,s,t,`Session lock is invalid.`);if(c.scope!==e)throw Z(t,`Session lock scope is invalid.`);if(c.hostname!==r||o(c.pid))throw new nF(e,c);await BF(t,c)}let c=await LF(t);throw c===void 0?Z(t,`Unable to acquire session lock.`):new nF(e,lF(cF,c,t,`Session lock is invalid.`))}async function v(e){return g(uF(e))}return{root:t,get:l,list:u,upsert:d,remove:h,getPreferred:f,setPreferred:p,clearPreferred:m,acquire:v}}function pF(e,t){let n=xF(t.scope),r=t.selection===`auto`||t.selection===`new`?void 0:vF(t.selection);function i(e){if(e.toLowerCase()!==n)throw new tF(`Session manager requested an unexpected payment scope.`)}async function a(n=!0){if(t.selection===`new`&&!r)return;let i=r??await e.getPreferred(t.scope);if(!i)return;let a=await e.get(i);if(!a)throw new tF(`Session ${i} does not exist.`);if(NF(a.channel,t.scope),!(n&&(a.status!==`open`||!a.channel.opened)))return r=i,a}return{async get(e){return i(e),(await a())?.channel},async set(n){if(i(oM(n)),NF(n,t.scope),r&&r.toLowerCase()!==n.channelId.toLowerCase())throw new tF(`Session manager selected ${r}, but attempted to store ${n.channelId}.`);r||t.onNewChannel?.(n);let a=await e.upsert({...t.context(),channel:n});r=a.channel.channelId,await e.setPreferred(t.scope,a.channel.channelId)},async delete(n){i(n);let o=await a(!1);if(!o)return;let s=t.context();await e.upsert({...o,...s,status:s.status===`closing`?`closing`:`stale`,channel:o.channel,spent:s.spent??o.spent,units:s.units??o.units}),s.status!==`closing`&&await e.clearPreferred(t.scope,o.channel.channelId),r=void 0}}}function mF(){let e=process.env.XDG_STATE_HOME||c.join(b.homedir(),`.local`,`state`);return c.join(e,`mppx`,`sessions`,`v1`)}function hF(e,t){return c.join(e.channels,`${t}.json`)}function gF(e,t){let n=g(`sha256`).update(t).digest(`hex`);return c.join(e.locks,`${n}.lock`)}function _F(e,t){let n=g(`sha256`).update(uF(t)).digest(`hex`);return c.join(e.preferred,`${n}.json`)}function vF(e){return lF(qx(),e,void 0,`Invalid session channel ID: ${e}.`).toLowerCase()}function yF(e,t,n){return lF(Kx(),e,n,`Invalid ${t}.`).toLowerCase()}function bF(e){if(!Number.isSafeInteger(e.chainId)||e.chainId<0)throw new tF(`Invalid session chain ID.`);return{payer:yF(e.payer,`session payer`),payee:yF(e.payee,`session payee`),token:yF(e.token,`session token`),escrow:yF(e.escrow,`session escrow`),chainId:e.chainId}}function xF(e){let t=bF(e);return[t.payee,t.token,t.escrow,t.chainId].join(`:`)}function SF(e,t){if(typeof e!=`string`)throw Z(t,`Session endpoint is invalid.`);let n;try{n=new URL(e)}catch(e){throw Z(t,`Session endpoint is invalid.`,e)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw Z(t,`Session endpoint must use HTTP or HTTPS.`);if(n.username||n.password)throw Z(t,`Session endpoint must not contain credentials.`);let r=e.indexOf(`#`);return r===-1?e:e.slice(0,r)}function CF(e,t){return lF(iF,e,t,`Session account is invalid.`)}function wF(e){return lM(TF(cM(e)))}function TF(e,t){return lF(rF,e,t,`Stored session channel is invalid.`)}function EF(e,t){let n=gS.safeParse(e);if(!n.success||!FD(n.data))throw Z(t,`Stored session challenge is invalid.`);return n.data}function DF(e,t,n){let r=lF(aF,e,n,`Stored session receipt is invalid.`);if(r.channelId!==t.toLowerCase())throw Z(n,`Stored session receipt has a different channel ID.`);if(r.reference.toLowerCase()!==t.toLowerCase())throw Z(n,`Stored session receipt has a different reference.`);if(BigInt(r.spent)>BigInt(r.acceptedCumulative))throw Z(n,`Stored session receipt spend exceeds its accepted amount.`);return r}function OF(e,t){let n=lF(oF,e,t,`Stored session is invalid.`),{receipt:r,...i}=n,a=n.channel,o=n.account;if(o.address.toLowerCase()!==a.descriptor.payer.toLowerCase())throw Z(t,`Stored session account does not match the channel payer.`);let s=r?DF(r,a.channelId,t):void 0,c=n.spent,l=BigInt(a.cumulativeAmount);if(BigInt(c)>l)throw Z(t,`Stored session spend exceeds its cumulative authorization.`);if(s&&BigInt(s.acceptedCumulative)>l)throw Z(t,`Stored receipt exceeds the cumulative authorization.`);if(Date.parse(n.updatedAt)<Date.parse(n.createdAt))throw Z(t,`Stored session timestamps are not monotonic.`);let u=SF(n.endpoint,t);if(u!==n.endpoint)throw Z(t,`Stored session endpoint contains a fragment.`);let d=EF(n.challenge,t);return MF(d,lM(a),t),{...i,account:o,channel:a,endpoint:u,challenge:d,...s&&{receipt:s}}}function kF(e){return{version:1,status:e.status,channel:lM(e.channel),account:e.account,endpoint:e.endpoint,challenge:e.challenge,...e.receipt&&{receipt:e.receipt},spent:BigInt(e.spent),units:e.units,createdAt:e.createdAt,updatedAt:e.updatedAt}}function AF(e,t,n){let r=wF(t.channel),i=CF(t.account,n),a=lM(e.channel);if(JSON.stringify(jF(r))!==JSON.stringify(jF(a)))throw Z(n,`Session update changed immutable channel identity.`);if(i.address.toLowerCase()!==e.account.address.toLowerCase())throw Z(n,`Session update changed the payer account.`)}function jF(e){return{channelId:e.channelId.toLowerCase(),descriptor:Object.fromEntries(Object.entries(e.descriptor).map(([e,t])=>[e,typeof t==`string`?t.toLowerCase():t])),escrow:e.escrow.toLowerCase(),chainId:e.chainId}}function MF(e,t,n){if(xj({...t.descriptor,chainId:t.chainId,escrow:t.escrow}).toLowerCase()!==t.channelId.toLowerCase())throw Z(n,`Session channel ID does not match its descriptor.`);let r=yF(e.request.recipient,`challenge recipient`,n),i=yF(e.request.currency,`challenge currency`,n);if(r!==t.descriptor.payee.toLowerCase())throw Z(n,`Session challenge payee does not match the channel.`);if(i!==t.descriptor.token.toLowerCase())throw Z(n,`Session challenge token does not match the channel.`);if(Zj(e,t.escrow).toLowerCase()!==t.escrow.toLowerCase())throw Z(n,`Session challenge escrow does not match the channel.`);if(qF(e.request.methodDetails)){let r=e.request.methodDetails;if(r.chainId!==void 0&&r.chainId!==t.chainId)throw Z(n,`Session challenge chain does not match the channel.`)}}function NF(e,t,n){let r=bF(t);if(e.descriptor.payer.toLowerCase()!==r.payer||e.descriptor.payee.toLowerCase()!==r.payee||e.descriptor.token.toLowerCase()!==r.token||e.escrow.toLowerCase()!==r.escrow||e.chainId!==r.chainId)throw Z(n,`Session channel does not match the selected payment scope.`)}function PF(e,t){return e?t&&Date.parse(t.timestamp)>=Date.parse(e.timestamp)?t:e:t}function FF(e,t,n){if(!Number.isFinite(t.getTime()))throw Z(n,`Session clock is invalid.`);let r=t.toISOString();return!e||Date.parse(r)>=Date.parse(e)?r:e}function IF(...e){return e.reduce((e,t)=>t>e?t:e,0n)}async function LF(e){let t;try{t=await C.readFile(e,`utf8`)}catch(t){if(JF(t,`ENOENT`))return;throw Z(e,`Unable to read session state.`,t)}try{return JSON.parse(t)}catch(t){throw Z(e,`Session state contains invalid JSON.`,t)}}async function RF(e,t){await C.mkdir(c.dirname(e),{recursive:!0,mode:448}),await C.chmod(c.dirname(e),448);let n=`${e}.tmp-${process.pid}-${_()}`,r;try{r=await C.open(n,`wx`,384),await r.writeFile(`${JSON.stringify(t,null,2)}\n`,`utf8`),await r.sync(),await r.close(),r=void 0,await C.rename(n,e),await C.chmod(e,384),await GF(c.dirname(e))}catch(t){throw await r?.close().catch(()=>void 0),await C.unlink(n).catch(()=>void 0),t instanceof tF?t:Z(e,`Unable to write session state.`,t)}}async function zF(e,t){let n;try{n=await C.open(e,`wx`,384),await n.writeFile(`${JSON.stringify(t)}\n`,`utf8`),await n.sync(),await n.close(),n=void 0,await GF(c.dirname(e))}catch(t){throw await n?.close().catch(()=>void 0),n&&await C.unlink(e).catch(()=>void 0),t}}async function BF(e,t){let n=VF(e);try{await C.link(e,n)}catch(t){if(JF(t,`EEXIST`)||JF(t,`ENOENT`))return;throw Z(e,`Unable to claim dead lock.`,t)}try{let r=await LF(n);if(r===void 0||lF(cF,r,n,`Session lock is invalid.`).token!==t.token)return;await WF(e,`Unable to reclaim dead lock.`)}finally{await WF(n,`Unable to release dead lock claim.`)}}function VF(e){return`${e}.reclaim`}async function HF(e){try{return await C.access(e),!0}catch(t){if(JF(t,`ENOENT`))return!1;throw Z(e,`Unable to inspect session lock.`,t)}}async function UF(e,t){let n=await LF(e);n!==void 0&&lF(cF,n,e,`Session lock is invalid.`).token===t.token&&await WF(e,`Unable to release session lock.`)}async function WF(e,t){try{await C.unlink(e),await GF(c.dirname(e))}catch(n){if(!JF(n,`ENOENT`))throw Z(e,t,n)}}async function GF(e){let t=await C.open(e,`r`);try{await t.sync()}finally{await t.close()}}function KF(e){try{return process.kill(e,0),!0}catch(e){if(JF(e,`ESRCH`))return!1;if(JF(e,`EPERM`))return!0;throw e}}function qF(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function JF(e,t){return e instanceof Error&&`code`in e&&e.code===t}function Z(e,t,n){return new tF(t,{...n!==void 0&&{cause:n},file:e})}Ox(),yT(),aN(),SM(),Tj(),dk(),ZO();const YF=p.object({status:p.enum([`opening`,`open`,`closing`,`stale`]),channelId:p.string(),account:p.string().optional(),payer:p.string(),payee:p.string(),authorizedSigner:p.string(),token:p.string(),escrow:p.string(),chainId:p.number(),cumulativeAmount:p.string(),confirmedSpend:p.string(),deposit:p.string(),units:p.number(),resourceUrl:p.string(),createdAt:p.string(),updatedAt:p.string()}),XF=p.object({channelId:p.string(),status:p.enum([`closed`,`already-closed`]),spent:p.string(),txHash:p.string().optional()}),ZF=p.object({closed:p.array(XF),failed:p.array(p.object({channelId:p.string(),message:p.string()}))});function QF(e){return e===`mainnet`?ve.id:ye.id}function $F(e){if(e===ve.id)return`mainnet`;if(e===ye.id)return`testnet`}function eI(e){let t={};for(let n of e??[]){let e=n.indexOf(`:`);if(e===-1)throw new L({code:`INVALID_HEADER`,message:`Invalid header format: ${n}`,exitCode:2});t[n.slice(0,e).trim()]=n.slice(e+1).trim()}return t}function tI(e){return{status:e.status,channelId:e.channel.channelId,...e.account.name&&{account:e.account.name},payer:e.channel.descriptor.payer,payee:e.channel.descriptor.payee,authorizedSigner:e.channel.descriptor.authorizedSigner,token:e.channel.descriptor.token,escrow:e.channel.escrow,chainId:e.channel.chainId,cumulativeAmount:e.channel.cumulativeAmount.toString(),confirmedSpend:e.spent.toString(),deposit:e.channel.deposit.toString(),units:e.units,resourceUrl:e.endpoint,createdAt:e.createdAt,updatedAt:e.updatedAt}}function nI(e,t){throw e instanceof L?e:e instanceof nF?new L({code:e.code,message:e.message,exitCode:e.exitCode,cause:e}):e instanceof tF?new L({code:e.code,message:e.message,exitCode:65,cause:e}):new L({code:t,message:e instanceof Error?e.message:String(e),exitCode:75,...e instanceof Error&&{cause:e}})}async function rI(e,t){let n=await sk(t??e.account.name);if(!UM(n.account,e.channel.descriptor))throw new L({code:`SESSION_ACCOUNT_MISMATCH`,message:`Account ${n.account.address} cannot sign for session ${e.channel.channelId}.`,exitCode:2});return n}async function iI(e,t){if(t.network&&QF(t.network)!==e)throw new L({code:`CHAIN_MISMATCH`,message:`Session uses chainId ${e}, not ${t.network}.`,exitCode:2});let n=t.network??$F(e),r=LO(t.rpcUrl,{network:n}),i=await zO({network:n,rpcUrl:r});if(i.id!==e)throw new L({code:`CHAIN_MISMATCH`,message:`Session uses chainId ${e}, but RPC is chainId ${i.id}.`,exitCode:2});return M({chain:i,transport:le(r)})}const aI=cb(`sessions`,{description:`Manage persistent payment sessions (list, view, close)`}).command(`list`,{description:`List persistent payment sessions`,options:p.object({account:p.string().optional().describe(`Filter by account name`),network:p.enum([`mainnet`,`testnet`]).optional().describe(`Filter by Tempo network`)}),output:p.object({sessions:p.array(YF)}),alias:{account:`a`},async run(e){try{let t=(await fF().list()).filter(t=>!(e.options.account&&t.account.name!==e.options.account||e.options.network&&t.channel.chainId!==QF(e.options.network))).map(tI),n={sessions:t};if(e.format===`json`&&e.formatExplicit)return e.ok(n);t.length===0&&console.log(`No sessions.`);for(let[e,n]of t.entries())e>0&&console.log(``),oI(n);return}catch(e){return nI(e,`SESSION_LIST_FAILED`)}}}).command(`view`,{description:`View a persistent payment session`,args:p.object({channelId:p.string().describe(`Full session channel ID`)}),output:YF,async run(e){try{let t=await fF().get(e.args.channelId);if(!t)throw new L({code:`SESSION_NOT_FOUND`,message:`Session ${e.args.channelId} was not found.`,exitCode:2});let n=tI(t);if(e.format===`json`&&e.formatExplicit)return e.ok(n);oI(n);return}catch(e){return nI(e,`SESSION_VIEW_FAILED`)}}}).command(`close`,{description:`Cooperatively close persistent payment sessions`,usage:[{suffix:`<channel-id> [options]`},{suffix:`--all --yes [options]`}],args:p.object({channelId:p.string().optional().describe(`Full session channel ID`)}),options:p.object({account:p.string().optional().describe(`Account name`),all:p.boolean().optional().default(!1).describe(`Close every matching session`),header:p.array(p.string()).optional().describe(`Add close request header (repeatable)`),network:p.enum([`mainnet`,`testnet`]).optional().describe(`Tempo network`),rpcUrl:p.string().optional().describe(`RPC endpoint (env: MPPX_RPC_URL)`),url:p.string().optional().describe(`Override the stored resource URL`),yes:p.boolean().optional().default(!1).describe(`Confirm closing every session`)}),output:p.union([XF,ZF]),alias:{account:`a`,header:`H`,rpcUrl:`r`},async run(e){if(e.options.all&&e.args.channelId)return e.error({code:`INVALID_SESSION_CLOSE`,message:`Specify a channel ID or --all, not both.`,exitCode:2});if(!e.options.all&&!e.args.channelId)return e.error({code:`INVALID_SESSION_CLOSE`,message:`Specify a channel ID or --all.`,exitCode:2});if(e.options.all&&!e.options.yes)return e.error({code:`CONFIRMATION_REQUIRED`,message:`Closing all sessions requires --yes.`,exitCode:2});if(e.options.all&&(e.options.header||e.options.rpcUrl||e.options.url))return e.error({code:`INVALID_SESSION_CLOSE`,message:`--header, --rpc-url, and --url only apply to a single session.`,exitCode:2});let t=fF();async function n(e,n={}){let r=await t.get(e);if(!r)throw new L({code:`SESSION_NOT_FOUND`,message:`Session ${e} was not found.`,exitCode:2});let i=dF(r.channel),a=await t.acquire(i);try{let r=await t.get(e);if(!r)throw new L({code:`SESSION_NOT_FOUND`,message:`Session ${e} was not found.`,exitCode:2});let a=await rI(r,n.account),o=await iI(r.channel.chainId,n);if(xj({...r.channel.descriptor,escrow:r.channel.escrow,chainId:r.channel.chainId}).toLowerCase()!==r.channel.channelId.toLowerCase())throw new L({code:`SESSION_STATE_INVALID`,message:`Stored descriptor does not derive the session channel ID.`,exitCode:65});if((await yM(o,r.channel.channelId,r.channel.escrow)).deposit===0n)return await t.remove(r.channel.channelId),{channelId:r.channel.channelId,status:`already-closed`,spent:r.spent.toString()};let s=SF(n.resourceUrl??r.endpoint),c={...a.source===`keychain`&&{name:a.accountName},address:a.account.address},l=(e=r.challenge)=>({status:`closing`,account:c,endpoint:s,challenge:e,...r.receipt&&{receipt:r.receipt},spent:r.spent,units:r.units}),u=await t.upsert({...l(),channel:r.channel}),d=eI(n.headers),f=u.challenge,p=await eF({channel:u.channel,challenge:u.challenge,fetch:async(e,t)=>globalThis.fetch(e,{...t,headers:{...d,...Mw(t?.headers)}}),input:s,spent:u.spent,async onChallenge(e){f=e,await t.upsert({...l(e),channel:u.channel})},manager:{account:a.account,client:o,channelStore:pF(t,{scope:i,selection:u.channel.channelId,context:()=>l(f)})}});return await t.remove(u.channel.channelId),{channelId:u.channel.channelId,status:`closed`,spent:p.receipt.spent,txHash:p.receipt.txHash}}finally{await a.release()}}try{if(e.options.all){let r=(await t.list()).filter(t=>(!e.options.account||t.account.name===e.options.account)&&(!e.options.network||t.channel.chainId===QF(e.options.network))),i=[],a=[];for(let t of r)try{i.push(await n(t.channel.channelId,{account:e.options.account,network:e.options.network}))}catch(e){a.push({channelId:t.channel.channelId,message:e instanceof Error?e.message:String(e)})}let o={closed:i,failed:a};if(a.length>0){for(let e of a)process.stderr.write(`${e.channelId}: ${e.message}\n`);process.exitCode=1}if(e.format===`json`&&e.formatExplicit)return e.ok(o);for(let e of i)console.log(`${e.channelId} ${e.status} ${e.spent}`);for(let e of a)console.log(`${e.channelId} failed ${e.message}`);return}let r=await n(e.args.channelId,{account:e.options.account,headers:e.options.header,network:e.options.network,resourceUrl:e.options.url,rpcUrl:e.options.rpcUrl});if(e.format===`json`&&e.formatExplicit)return e.ok(r);console.log(`${r.channelId} ${r.status} ${r.spent}`),r.txHash&&console.log(` transaction ${r.txHash}`);return}catch(e){return nI(e,`SESSION_CLOSE_FAILED`)}}});function oI(e){console.log(e.channelId),console.log(` status ${e.status}`),console.log(` cumulative amount ${e.cumulativeAmount}`),console.log(` confirmed spend ${e.confirmedSpend}`),console.log(` deposit ${e.deposit}`),console.log(` chain ${e.chainId}`),console.log(` payer ${e.payer}`),console.log(` payee ${e.payee}`),console.log(` token ${e.token}`),console.log(` resource ${e.resourceUrl}`)}Ox(),aN(),hN(),TN(),GE(),dk(),ZO();function sI(e,t){if(t&&e!==`auto`&&t.toLowerCase()!==e.toLowerCase())throw new L({code:`SESSION_SELECTION_CONFLICT`,message:`--session and -M channel= select different sessions.`,exitCode:2});let n=t??e;if(n===`auto`||n===`new`)return n;if(/^0x[0-9a-fA-F]{64}$/.test(n))return n.toLowerCase();throw new L({code:`INVALID_SESSION`,message:`Session must be auto, new, or a 32-byte channel ID.`,exitCode:2})}function cI(e){let t=e.allowCustomEscrow;if(t!==void 0){if(t===`true`)return!0;if(t===`false`)return!1;throw new L({code:`INVALID_ALLOW_CUSTOM_ESCROW`,message:`allowCustomEscrow must be true or false.`,exitCode:2})}}function lI(e,t,n,r){if(t.deposit!==void 0)return t.deposit;let i=e.request.suggestedDeposit;return typeof i==`string`?se(BigInt(i),r):n?`10`:void 0}function uI(e,t){if(!e)return;if(e.challengeId!==t.challenge.id)throw Error(`Session receipt challenge does not match the paid request.`);if(e.channelId.toLowerCase()!==t.channelId.toLowerCase())throw Error(`Session receipt channel does not match the selected channel.`);let n=BigInt(e.acceptedCumulative),r=BigInt(e.spent);if(n>t.cumulative)throw Error(`Session receipt exceeds the local cumulative authorization.`);if(r>n)throw Error(`Session receipt spend exceeds its accepted authorization.`);return e}function dI(e,t){return e.channel.channelId.toLowerCase()===t.toLowerCase()}function fI(e){if(e.trim()!==`[DONE]`){if(e.length===0){process.stdout.write(`
|
|
243
243
|
`);return}try{let t=JSON.parse(e);process.stdout.write(t.token??t.choices?.[0]?.delta?.content??e)}catch{process.stdout.write(e)}}}async function pI(e){let{options:t}=e,n=await sk(t.account),r=LO(t.rpcUrl,{network:t.network}),i=await zO({network:t.network,rpcUrl:r}),a=M({chain:i,transport:le(r)}),o=cI(e.methodOptions),s=await LM({allowCustomEscrow:o,challenge:e.challenge,getClient:async()=>a});if(s.chainId!==i.id)throw new L({code:`CHAIN_MISMATCH`,message:`Challenge requires chainId ${s.chainId}, but RPC is chainId ${i.id}.`,exitCode:2});let{decimals:c}=await UO(a,s.token,n.account.address,{requireDecimals:!0}),l={payer:n.account.address,payee:s.payee,token:s.token,escrow:s.escrow,chainId:s.chainId},u=sI(t.session,e.methodOptions.channel),d=e.registry??fF(),f=await d.acquire(l).catch(e=>{throw e instanceof nF?new L({code:e.code,message:e.message,exitCode:e.exitCode,cause:e}):e});try{let r=u===`auto`?await d.getPreferred(l):u===`new`?void 0:u,s=r?await d.get(r):void 0;if(r&&!s)throw new L({code:`SESSION_NOT_FOUND`,message:`Session ${r} was not found.`,exitCode:2});if(s&&u!==`auto`&&(s.status!==`open`||!s.channel.opened||uF(dF(s.channel))!==uF(l)||!UM(n.account,s.channel.descriptor)))throw new L({code:`SESSION_MISMATCH`,message:`Session ${s.channel.channelId} cannot be used for this account and challenge.`,exitCode:2});let f=s?.status===`open`&&s.channel.opened?s:void 0,p=f?`open`:`opening`,m=e.challenge,h=f?.receipt,g=f?.spent??0n,_=f?.units??0,v=SF(e.endpoint),y={...n.source===`keychain`&&{name:n.accountName},address:n.account.address},b=pF(d,{scope:l,selection:u,context:()=>({status:p,account:y,endpoint:v,challenge:m,...h&&{receipt:h},spent:g,units:_}),onNewChannel(){p=`opening`,h=void 0,g=0n,_=0}}),{signal:x,...S}=e.init,C={...S,...x&&{signal:x},onReceipt(e){h=e,g=g>BigInt(e.spent)?g:BigInt(e.spent),_=Math.max(_,e.units??0)}};if(e.challengeResponse.status!==402||e.challengeResponse.bodyUsed)throw Error(`Session manager requires an unconsumed 402 challenge response.`);let w=!0,T=wN({account:n.account,allowCustomEscrow:o,bootstrap:!1,client:a,channelStore:b,credentialContext:e.credentialContext,decimals:c,maxDeposit:lI(e.challenge,e.methodOptions,HO(i),c),fetch:async(t,n)=>w?(w=!1,e.challengeResponse):e.fetch(t,n)});f&&pN(T).rehydrate({channel:f.channel,challenge:e.challenge,input:e.fetchInput,spent:f.spent});let{onReceipt:ee,...E}=C,D=await T.fetch(e.fetchInput,E);if(t.fail&&D.status>=400)throw new L({code:`HTTP_ERROR`,message:`HTTP error ${D.status}`,exitCode:22});if(D.status===402)throw new L({code:`PAYMENT_REJECTED`,message:`Payment rejected.`,exitCode:75});EO(D,{include:!1,verbose:t.include?Math.max(t.verbose,2):t.verbose,silent:t.silent}),m=D.challenge??e.challenge;let O=T.channelId;if(!O)throw Error(`Session manager did not select a channel.`);if(h=uI(D.receipt,{challenge:m,channelId:O,cumulative:T.cumulative})??h,FE(D)){let t=pN(T).consumeSseResponse(e.fetchInput,D,{onReceipt:ee,signal:e.init.signal??void 0});for await(let e of t)fI(e)}else process.stdout.write(Buffer.from(await D.arrayBuffer()));let k=await d.get(O);if(!k||!dI(k,O))throw Error(`Session ${O} was not persisted.`);if(h){let e=uI(h,{challenge:m,channelId:O,cumulative:T.cumulative});if(!e)throw Error(`Session receipt validation failed.`);h=e,g=g>BigInt(e.spent)?g:BigInt(e.spent),_=Math.max(_,e.units??0),p=`open`}await d.upsert({status:p,channel:k.channel,account:y,endpoint:v,challenge:m,...h&&{receipt:h},spent:g,units:_}),t.silent||(e.info(`Session retained ${O}\n`),e.info(`Close with: mppx sessions close ${O}\n`))}finally{await f.release()}}ZO();function Q(e,t){return t?{label:e,detail:t,severity:`pass`}:{label:e,severity:`pass`}}function $(e,t,n){let r={label:e,severity:`fail`};return t&&(r.detail=t),n&&(r.hint=n),r}function mI(e,t,n){let r={label:e,severity:`warn`};return t&&(r.detail=t),n&&(r.hint=n),r}function hI(e,t,n){let r=t?{label:e,detail:t,severity:`skip`}:{label:e,severity:`skip`};return n&&(r.hint=n),r}const gI={pass:X.green(`✓`),fail:X.red(`✗`),warn:X.yellow(`⚠`),suggested:X.cyan(`◇`),skip:X.dim(`○`)};function _I(e){let t=gI[e.severity],n=e.detail?`${e.label} (${e.detail})`:e.label;console.log(` ${t} ${n}`),e.hint&&e.severity!==`pass`&&console.log(X.dim(` → ${e.hint}`))}function vI(e){console.log(`\n${X.bold(e)}`)}function yI(e,t){for(let n of e)_I(n),n.severity===`pass`?t.passed++:n.severity===`fail`?t.failed++:n.severity===`warn`?t.warnings++:n.severity===`suggested`?t.suggested++:n.severity===`skip`&&t.skipped++}async function bI(e,t,n=15e3){let r=new AbortController,i=setTimeout(()=>r.abort(),n);try{return await fetch(e,{...t,redirect:`error`,signal:r.signal})}finally{clearTimeout(i)}}function xI(e){return e<1024?`${e}B`:e<1048576?`${(e/1024).toFixed(1)}KB`:`${(e/1048576).toFixed(1)}MB`}const SI=new Set([`get`,`post`,`put`,`patch`,`delete`,`head`,`options`,`trace`]);function CI(e){return typeof e==`string`&&/^0x[0-9a-fA-F]{40}$/.test(e)}function wI(e){return typeof e==`string`&&/^(0|[1-9][0-9]*)$/.test(e)}function TI(e){let t=e.indexOf(`:`);if(t<1)return null;let n=e.slice(0,t).toLowerCase();if(!SI.has(n))return null;let r=e.slice(t+1);if(!r)return null;let i=r.startsWith(`/`)?r:`/`+r;return{method:n.toUpperCase(),path:i}}function EI(e,t){if(e){try{let n=JSON.parse(e);if(n&&typeof n==`object`&&!Array.isArray(n)){let e=Object.keys(n);if(e.length>0&&e.every(e=>e.startsWith(`/`))){let e=n[t];return e===void 0?void 0:typeof e==`string`?e:JSON.stringify(e)}}}catch{}return e}}function DI(e){if(!e)return{};let t={};for(let n of e){let e=n.indexOf(`:`);e>0&&(t[n.slice(0,e).trim().toLowerCase()]=n.slice(e+1).trim())}return t}async function OI(e){let t=new URL(`openapi.json`,e.replace(/\/?$/,`/`)).href;try{let e=await bI(t,{});if(!e.ok)return{error:`HTTP ${e.status}`};let n=await e.text();return{doc:JSON.parse(n),raw:n}}catch(e){return e instanceof DOMException&&e.name===`AbortError`?{error:`Request timed out after 15s`}:e instanceof SyntaxError?{error:`Invalid JSON`}:{error:e.message}}}async function kI(e){let t=new URL(`/llms.txt`,e).href,n=`llms.txt found and nonempty`;try{let e=await bI(t,{});if(!e.ok)throw Error(`HTTP ${e.status}`);let r=(await e.text()).trim();if(!r)throw Error(`Empty document`);let i=e.headers.get(`content-type`)?.split(`;`)[0]?.trim().toLowerCase();if(i&&!i.startsWith(`text/`)||i===`text/html`||/^<(?:!doctype html|html|head|body)\b/i.test(r))throw Error(`Expected text documentation, received non-text or HTML content`);return Q(n,t)}catch(e){return{label:n,detail:`${t}: ${e.message}`,severity:`suggested`,hint:`Serve nonempty text documentation at /llms.txt.`}}}function AI(e){let t=[],n=[],r=e.paths;if(!r)return[];for(let[e,i]of Object.entries(r)){let r=NI(i.parameters);for(let[a,o]of Object.entries(i)){if(!SI.has(a)||!o||typeof o!=`object`||Array.isArray(o))continue;let i=o,s=PI(r,NI(i.parameters)),c=s.length>0?s:void 0;if(i[`x-payment-info`]){let n=i[`x-payment-info`];t.push({method:a.toUpperCase(),path:e,amount:n.amount,parameters:c})}else{let t=i.responses;t&&`402`in t&&n.push({method:a.toUpperCase(),path:e,parameters:c})}}}return t.length>0?t:n}function jI(e,t){let n=e.paths;if(!n)return;let r=n[t.path];if(!r)return;let i=r[t.method.toLowerCase()];if(!i?.requestBody)return;let a=i.requestBody.content?.[`application/json`];if(!a)return;if(a.example)return JSON.stringify(a.example);if(a.examples&&typeof a.examples==`object`){let e=Object.values(a.examples)[0];if(e?.value)return JSON.stringify(e.value)}let o=a.schema,s=new Set;if(o=II(o,e,s),!o||o.type!==`object`)return;let c=LI(o,e,s);if(c&&typeof c==`object`&&Object.keys(c).length>0)return JSON.stringify(c)}function MI(e,t,n){if(!t.path.startsWith(`/`)||t.path.startsWith(`//`)||t.path.includes(`\\`))throw Error(`OpenAPI path must be an origin-relative path: ${t.path}`);let r=t.path;t.parameters&&(r=FI(r,t.parameters));let i=r.startsWith(`/`)?r.slice(1):r,a=new URL(e.replace(/\/?$/,`/`)),o=new URL(i,a);if(o.origin!==a.origin)throw Error(`OpenAPI path must be an origin-relative path: ${t.path}`);let s=o.href;if(n){let e=new URL(s);for(let t of n){let[n,...r]=t.split(`=`);n&&e.searchParams.set(n,r.join(`=`))}s=e.href}return s}function NI(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(!n||typeof n!=`object`)continue;let e=n;e.in===`path`&&typeof e.name==`string`&&t.push(e.schema&&typeof e.schema==`object`?{name:e.name,in:`path`,schema:e.schema,example:e.example}:{name:e.name,in:`path`,example:e.example})}return t}function PI(e,t){let n=[...e];for(let e of t){let t=n.findIndex(t=>t.name===e.name);t>=0?n[t]=e:n.push(e)}return n}function FI(e,t){return e.replace(/\{([^}]+)\}/g,(e,n)=>{let r=t.find(e=>e.name===n&&e.in===`path`);if(!r)return e;let i=r.example??r.schema?.example??r.schema?.default;return i===void 0?e:encodeURIComponent(String(i))})}function II(e,t,n){if(!e||typeof e.$ref!=`string`)return e;if(n?.has(e.$ref))return;let r=e.$ref.replace(/^#\//,``).split(`/`),i=t;for(let e of r)if(i&&typeof i==`object`)i=i[e];else return;return i}function LI(e,t,n){let r=II(e,t,n)??e;if(r.const!==void 0)return r.const;if(r.example!==void 0)return r.example;if(r.default!==void 0)return r.default;switch(r.type){case`string`:return r.enum&&Array.isArray(r.enum)?r.enum[0]:r.format===`email`?`test@example.com`:r.format===`uuid`?`00000000-0000-0000-0000-000000000000`:r.format===`uri`||r.format===`url`?`https://example.com`:r.format===`date`?`2026-01-01`:r.pattern===`^\\d{5}(?:-\\d{4})?$`?`10001`:r.pattern===`^[A-Z]{2}$`?`US`:`test`;case`number`:case`integer`:return r.minimum??1;case`boolean`:return!0;case`array`:return[];case`object`:{let e=r.properties;if(!e)return{};let i=r.required||[],a={};for(let r of i){let i=e[r];i&&(a[r]=LI(i,t,n))}return a}default:return null}}_S(),Fx(),aw(),ZO();const RI=Object.values(Be);function zI(e){let t=e.request.methodDetails;return typeof t?.chainId==`number`&&RI.find(e=>e.id===t.chainId)?.testnet===!0}async function BI(e,t,n,r){let i=[],a;try{a=MI(e,t,r?.query)}catch(e){return i.push($(`Request URL valid`,e.message)),{results:i}}let o=DI(r?.extraHeaders),s;r?.body&&!r.bodyIsAutogenerated&&(s=r.body,o[`content-type`]||=`application/json`);let c;try{let e={method:t.method};Object.keys(o).length>0&&(e.headers=o),s&&(e.body=s),c=await bI(a,e)}catch(e){return i.push($(`Request failed`,e.message)),{results:i}}if(!s&&(c.status===400||c.status===422)){let e=r?.body??(r?.discoveryDoc?jI(r.discoveryDoc,t):void 0);if(e){n&&console.log(X.dim(` Retrying with body: ${e}`)),s=e,o[`content-type`]=`application/json`;try{c=await bI(a,{method:t.method,headers:o,body:s})}catch(e){return i.push($(`Request failed`,e.message)),{results:i}}}}if(c.status!==402)return c.status===200?i.push(hI(`Returns 402 without credentials`,`Got 200 (endpoint may not require payment in all cases)`)):c.status===401||c.status===403?i.push(hI(`Returns 402 without credentials`,`Got ${c.status} (endpoint requires auth before payment gate)`)):i.push($(`Returns 402 without credentials`,`Got ${c.status} instead`,c.status===400||c.status===422?`Server requires a valid request body before returning 402. Add a requestBody schema with examples to your OpenAPI doc, or use --body.`:`Endpoints that require payment must return HTTP 402 with a WWW-Authenticate: Payment header when no valid credential is provided.`)),{results:i};i.push(Q(`Returns 402 without credentials`));let l=c.headers.get(q.wwwAuthenticate),u=ZI(c);if(!l)return u.length>0?(i.push(hI(`Not an MPP endpoint`,`No WWW-Authenticate header — x402 protocol detected`)),i.push(...u)):i.push(hI(`Not an MPP endpoint`,`No WWW-Authenticate header (may be x402 or other protocol)`)),{results:i};if(u.length>0&&i.push(...u),!l.startsWith(`${Ax.payment} `))return i.push(hI(`Not an MPP endpoint`,`WWW-Authenticate scheme is not Payment`)),{results:i};i.push(Q(`WWW-Authenticate header present`,`Payment scheme`));let d;try{d=cS(c)}catch(e){let t=e.message;i.push($(`Challenge parseable`,t,`The Payment challenge must include a request="<base64url>" parameter containing JSON-encoded payment details.`));let n=l.slice(l.indexOf(` `)+1);return i.push(mI(`Received`,n.length>200?n.slice(0,200)+`...`:n)),{results:i}}if(d.length===0)return i.push($(`Challenge parseable`,`No Payment challenges found in header`)),{results:i};let f=d.map(e=>`${e.method}/${e.intent}`).join(`, `);i.push(Q(`Challenge parseable`,d.length===1?f:`${d.length} methods: ${f}`));let p=new URL(e).hostname;VI(d,i),HI(d,i),UI(d,i),WI(d,p,i);let m=d.length>1;for(let e of d){let t=e.request;GI(e,t,i,m)}if(n)for(let e of d)console.log(X.dim(` Challenge (${e.method}/${e.intent}): ${JSON.stringify(e,null,2)}`));return{results:i,resolvedBody:s,challenges:d}}function VI(e,t){let n=e.filter(e=>!e.id);n.length===0?t.push(Q(`Challenge has id`)):t.push($(`Challenge has id`,`${n.length} missing`,`Every challenge must include a unique id field.`))}function HI(e,t){let n=e.filter(e=>!e.realm);n.length===0?t.push(Q(`Challenge has realm`)):t.push($(`Challenge has realm`,`${n.length} missing`,`Set realm to your server's hostname.`))}function UI(e,t){let n=new Date;for(let r of e){if(!r.expires){t.push(mI(`Challenge has expiration`,`missing expires field`,`Add an expires field (ISO 8601) to prevent replay attacks.`));return}if(new Date(r.expires)<=n){t.push($(`Challenge expires in the future`,`Expired at ${r.expires}`,`The expires timestamp must be in the future.`));return}}let r=Math.min(...e.map(e=>new Date(e.expires).getTime()-n.getTime()));t.push(Q(`Challenge expires in the future`,`${Math.round(r/6e4)}m from now`))}function WI(e,t,n){let r=e=>e.toLowerCase().replace(/\.$/,``),i=r(t),a=[...new Set(e.map(e=>e.realm??``))].filter(e=>{let t=r(e);return!t||t!==i&&!i.endsWith(`.${t}`)});a.length>0?n.push($(`Realm matches server hostname`,`realm="${a[0]}" vs host="${t}"`,`Set the realm to your production hostname (or base domain) in the challenge.`)):n.push(Q(`Realm matches server hostname`))}function GI(e,t,n,r){let i=r?`[${e.method}] `:``;e.method===jx.tempo?KI(t,i,n,e):e.method===jx.stripe?qI(t,i,n):e.method===jx.evm&&JI(e,t,i,n)}function KI(e,t,n,r){let i=e.methodDetails,a=Array.isArray(i?.splits)&&i.splits.length>0;if(CI(e.recipient)?n.push(Q(`${t}Valid recipient address`)):e.recipient===void 0&&a?n.push(Q(`${t}Uses splits (no single recipient)`)):e.recipient===void 0?n.push($(`${t}Valid recipient address`,`Missing recipient (and no splits defined)`,`Set request.recipient to a valid 0x address, or use methodDetails.splits for multiple recipients.`)):n.push($(`${t}Valid recipient address`,`Got: ${String(e.recipient)}`,`Set request.recipient to a valid 0x-prefixed 40-hex-char address.`)),CI(e.currency)){let e=zI(r)?`testnet`:`mainnet`;n.push(Q(`${t}Valid currency address`,e))}else n.push($(`${t}Valid currency address`,`Got: ${String(e.currency)}`,`Set request.currency to a valid token address.`));YI(e,t,n,`token's smallest unit (e.g. "10000" = $0.01 for 6-decimal tokens)`)}function qI(e,t,n){YI(e,t,n,`currency's smallest unit (e.g. "100" = $1.00 for USD)`);let r=e.currency;typeof r==`string`&&/^[a-z]{3}$/i.test(r)?n.push(Q(`${t}Valid currency code`,r.toUpperCase())):n.push($(`${t}Valid currency code`,r?`Got: ${r}`:`missing`,`Must be a three-letter ISO currency code (e.g. "usd").`));let i=e.methodDetails,a=i?.networkId;a?n.push(Q(`${t}Has networkId`,a.slice(0,20))):n.push($(`${t}Has networkId`,`Missing methodDetails.networkId`,`Set methodDetails.networkId to your Stripe Business Network profile ID.`));let o=i?.paymentMethodTypes;Array.isArray(o)&&o.length>0?n.push(Q(`${t}Has paymentMethodTypes`,o.join(`, `))):n.push($(`${t}Has paymentMethodTypes`,`Missing or empty`,`Set methodDetails.paymentMethodTypes to an array (e.g. ["card"]).`))}function JI(e,t,n,r){if(CI(t.recipient)?r.push(Q(`${n}Valid recipient address`)):r.push($(`${n}Valid recipient address`,`Got: ${String(t.recipient)}`,`Set request.recipient to a valid 0x-prefixed Ethereum address.`)),CI(t.currency)){let t=zI(e)?`testnet`:`mainnet`;r.push(Q(`${n}Valid currency address`,t))}else r.push($(`${n}Valid currency address`,`Got: ${String(t.currency)}`,`Set request.currency to a valid ERC-20 token contract address.`));YI(t,n,r,`token's smallest unit`);let i=t.methodDetails?.chainId;typeof i==`number`&&i>0?r.push(Q(`${n}Has chainId`,String(i))):r.push($(`${n}Has chainId`,`Missing methodDetails.chainId`,`Set methodDetails.chainId to the EVM chain ID.`))}function YI(e,t,n,r){wI(e.amount)?n.push(Q(`${t}Amount is valid integer string`)):e.amount===void 0||e.amount===null?n.push($(`${t}Amount is valid integer string`,`Missing amount`,`Set request.amount to a string of digits in the ${r}.`)):n.push($(`${t}Amount is valid integer string`,`Got: ${String(e.amount)}`,`request.amount must be a string of digits (no decimals, no prefix).`))}async function XI(e,t,n){let r=[],i=MI(e,t,n?.query),a={[q.authorization]:`${Ax.payment} dGhpcyBpcyBnYXJiYWdl`},o;n?.body&&(o=n.body,a[`content-type`]=`application/json`);try{let e=await bI(i,{method:t.method,headers:a,body:o??null});e.status===402?(r.push(Q(`Malformed credential returns 402`,`not 500`)),e.headers.get(q.wwwAuthenticate)?.startsWith(`${Ax.payment} `)?r.push(Q(`Error response includes fresh challenge`)):r.push(mI(`Error response includes fresh challenge`,`No WWW-Authenticate header`,`When rejecting an invalid credential, respond with 402 and include a fresh WWW-Authenticate: Payment challenge so the client can retry.`))):e.status>=500?r.push($(`Malformed credential returns 402`,`Got ${e.status} (server error)`,`When the Authorization header contains an invalid credential, respond with 402 (not 500). Catch credential validation errors and return a fresh challenge.`)):r.push(mI(`Malformed credential returns 402`,`Got ${e.status}`,`When the Authorization header contains an invalid Payment credential, respond with 402 and a fresh WWW-Authenticate challenge. Returning ${e.status} prevents the client from retrying with a valid payment.`))}catch(e){r.push($(`Error handling test`,e.message))}return r}function ZI(e){let t=[],n=e.headers.get(`payment-required`)??e.headers.get(`x-payment-required`);if(!n)return t;let r=e.headers.get(`payment-required`)?`PAYMENT-REQUIRED`:`X-Payment-Required`;try{let e=QC(n);t.push(Q(`x402 payment challenge`,`${r} with valid x402 v${e.x402Version} payload`))}catch{t.push(mI(`x402 payment challenge`,`${r} header present but failed schema validation`,`Ensure the PAYMENT-REQUIRED header contains a valid base64-encoded JSON payload matching the x402 schema.`))}return t}const QI=[`No discovery document found, endpoints could not be automatically found.`,`MPP servers must serve an OpenAPI document at /openapi.json with x-payment-info extensions.`,`The MPP SDKs can automatically expose this endpoint. See https://mpp.dev/sdk.`,``,`To test a specific endpoint: mppx validate <url> --endpoint POST:/your/path`].join(`
|
|
244
244
|
`);Zx();const $I={fundingCurrency:J.optional(J.string()),method:J.string(),reference:J.string(),externalId:J.optional(J.string()),subscriptionId:J.optional(J.string()),status:J.literal(`success`),timestamp:Hx()};J.object($I);const eL=J.looseObject($I);function tL(e){let t=k.toString(e);return nL(JSON.parse(t))}function nL(e){return eL.parse(e)}_S(),Fx(),rP(),CO(),_O(),dk(),ZO();async function rL(e,t,n){try{n||console.log(X.dim(` Provisioning testnet wallet and funding via faucet...`));let t=he(),r=_e(t),i=M({chain:ye,transport:le()});if(FD(e)){let{chainId:t,operator:n}=e.request.methodDetails??{};if(t===ye.id&&typeof n==`string`&&ue(n)&&n!==me){let e=await Fe.faucet.fund(i,{account:n});await Promise.all(e.map(e=>Me(i,{hash:e})))}}let a=await Fe.faucet.fund(i,{account:r});await Promise.all(a.map(e=>Me(i,{hash:e}))),n||console.log(X.dim(` Using wallet: ${r.address}`));let o=e.request.currency;if(typeof o==`string`)for(let e=0;e<10;e++){let e=await Fe.token.getBalance(i,{account:r.address,token:o}).catch(()=>void 0);if(e&&e.amount>0n)break;await new Promise(e=>setTimeout(e,500))}return{methods:[...eP({account:r})]}}catch(e){t&&console.log(X.dim(` Provisioning failed: ${e.message}`));return}}async function iL(){let e=ik(),{isTempoAccount:t}=await Promise.resolve().then(()=>(ZO(),wO)),{resolveTempoAccount:n}=await Promise.resolve().then(()=>(LP(),iP));if(t(e)){let t=n(e);if(t)return t.wallet_address}try{return(await ck()).address}catch{return}}function aL(e){try{let{execSync:t}=m,n=t(`stripe config --list`,{encoding:`utf8`,timeout:5e3}).match(/test_mode_api_key\s*=\s*'([^']+)'/);if(n?.[1])return e&&console.log(X.dim(` Using Stripe test key from stripe CLI`)),n[1]}catch{}}async function oL(e,t,n){let r=M({chain:e,transport:le()}),[i,a,o]=await Promise.all([n?De(r,{address:t,abi:oe,functionName:`balanceOf`,args:[n]}):0n,De(r,{address:t,abi:oe,functionName:`decimals`}),De(r,{address:t,abi:oe,functionName:`symbol`}).catch(()=>void 0)]);return{balance:i,decimals:a,symbol:o??void 0}}function sL(e){return Object.values(Be).find(t=>t.id===e)}function cL(e,t){return`$${(Number(e)/10**t).toFixed(2)}`}function lL(e){switch(e.method){case jx.evm:return`no EVM payment plugin yet`;case`solana`:return`no Solana payment plugin yet`;default:return`no plugin for ${e.method}/${e.intent}`}}async function uL(e,t,n,r){let i=[],a=MI(e,t,r.query),o=DI(r.extraHeaders),s;r.body&&(s=r.body,o[`content-type`]=`application/json`);let c;try{c=await bI(a,{method:t.method,headers:o,body:s??null})}catch(e){return i.push($(`Payment: fetch challenge`,e.message)),i}if(c.status!==402)return i.push(hI(`Payment: skipped`,`Endpoint returned ${c.status}`)),i;let l;try{l=cS(c)}catch(e){return i.push($(`Payment: parse challenge`,e.message)),i}if(l.length===0)return i.push($(`Payment: parse challenge`,`No Payment challenges in response`)),i;let u=[];WI(l,new URL(a).hostname,u);let d=u.filter(e=>e.severity===`fail`);if(d.length>0)return i.push(...d),r.onResults?.(d),i;let f=await YP().catch(()=>void 0),p=l.find(e=>{if(e.method!==jx.tempo||qP(f?.config)?.some(t=>t.name===e.method&&t.intent===e.intent))return!1;let t=e.request.methodDetails;return typeof t?.chainId==`number`&&t.chainId!==vO.mainnet});if(p){let e=await rL(p,n,r.silent);if(e)try{await hL(i,await wT({methods:e.methods,polyfill:!1,async onChallenge(e,{createCredential:t}){ZP(p,e);let n=await t(await UP(e,f?.config.extensions));return i.push(Q(`Payment: submitted`,`ephemeral testnet wallet`)),n},fetch:(e,t)=>bI(e,t??{},3e4)}).fetch(a,{method:t.method,headers:o,body:s??null}),n,ye)}catch(e){i.push($(`Payment: create credential`,e.message))}else i.push($(`Payment: auto-provision wallet`,`Failed to create and fund testnet wallet`));r.onResults?.(i)}let m=r.interactive,h=process.env.MPPX_STRIPE_SECRET_KEY??aL(n),g=h?.startsWith(`sk_test_`)||h?.startsWith(`rk_test_`),_=new Set([jx.tempo,jx.evm,jx.stripe]);for(let e of l){if(e===p||!_.has(e.method))continue;r.silent||console.log(``);let c=i.length,l=()=>{r.onResults&&i.length>c&&r.onResults(i.slice(c))},u=`Payment [${e.method}]`;try{switch(e.method){case jx.tempo:case jx.evm:await dL(e,u,{results:i,url:a,endpoint:t,fetchHeaders:o,fetchBody:s,verbose:n,loaded:f,isInteractive:m,options:r});break;case jx.stripe:await fL(e,u,{results:i,url:a,endpoint:t,fetchHeaders:o,fetchBody:s,verbose:n,loaded:f,isInteractive:m,isStripeTestKey:g??!1,stripeKey:h,options:r})}}finally{l()}}return i}async function dL(e,t,n){let{results:r,url:i,endpoint:a,fetchHeaders:o,fetchBody:s,verbose:c,loaded:l,options:u}=n,d=e.request,f=d.methodDetails,p=wI(d.amount)?BigInt(d.amount):void 0,m=f?.decimals??d.decimals??6,h=d.currency,{plugin:g,method:_}=WP(e,l?.config);if(!g&&!_){r.push(hI(t,lL(e)));return}let v;if(!_){try{v=await iL()}catch{}if(!v){r.push(hI(t,`no wallet configured. Run "mppx account create" to create one.`));return}}let y;if(e.method===jx.tempo){let e=f?.chainId??(_?void 0:ve.id);e!==void 0&&(y=e===ye.id?ye:sL(e))}else if(e.method===jx.evm){let e=f?.chainId;e&&(y=sL(e))}if(p&&h&&!y){r.push(hI(t,`unable to resolve payment chain and verify token decimals`));return}let b;if(p&&h&&y)try{let n;if(e.method===jx.tempo){let e=await UO(M({chain:y,transport:le()}),h,v??me,{requireDecimals:!0});n=e.balance,m=e.decimals,b=e.symbol}else{let e=await oL(y,h,v);n=e.balance,m=e.decimals,b=e.symbol}if(v&&n<p){let e=cL(p,m),i=cL(n,m),a=b??`tokens`;r.push(hI(t,`insufficient balance (have ${i}, need ${e} ${a} on ${y.name})`,`Fund wallet ${v} with at least ${e} ${a} on ${y.name}.`));return}}catch(e){c&&console.log(X.dim(` Balance check skipped: ${e.message}`)),r.push(hI(t,`unable to verify token balance and decimals from the payment chain`));return}let x=p?cL(p,m):`unknown amount`,S=b??(h?.length===3?h.toUpperCase():``),C=y?.name,w=`${x}${S?` ${S}`:``}${C?` on ${C}`:``}`;if(u.silent||console.log(X.dim(v?` Attempting payment with wallet ${v}`:` Attempting payment with configured method`)),!_&&y?.testnet)u.silent||console.log(X.dim(` Auto-approved: ${w} (testnet)`));else if(!u.yes&&!n.isInteractive){r.push(hI(t,`non-interactive mode, use --yes to approve`));return}else if(u.yes)u.silent||console.log(X.dim(` Auto-approved: ${w}`));else if(!await FO(` ${X.yellow(`Pay`)} ${w}. Continue?`,!1)){r.push(hI(t,`declined`));return}let T,ee,E;if(g)try{let t=await g.setup({challenge:e,options:{network:`mainnet`},methodOpts:{}});T=t.methods,ee=t.createCredential,E=t.credentialContext}catch(e){r.push(hI(t,e.message));return}else T=[_];if(_&&FD(e)){let n=!0,u=wT({methods:[_],polyfill:!1,async onChallenge(n,{createCredential:i}){let a=await UP(n,l?.config.extensions);ZP(e,n);let o=await i(a);return r.push(Q(`${t}: submitted`)),o},fetch:async(t,r)=>n?(n=!1,new Response(null,{status:402,headers:{[q.wwwAuthenticate]:$x(e)}})):bI(t,r??{},3e4)});try{await hL(r,await u.fetch(i,{method:a.method,headers:o,body:s??null}),c,y)}catch(e){r.push($(t,e.message))}return}let D=await pL(e,T,ee,t,r,l?.config.extensions,E);D&&(g?.prepareCredentialRequest?.({challenge:e,credential:D,headers:o}),await mL(r,i,a,D,o,s,c,y,fS(e)))}async function fL(e,t,n){let{results:r,url:i,endpoint:a,fetchHeaders:o,fetchBody:s,verbose:c,loaded:l,options:u,stripeKey:d}=n,{plugin:f,method:p}=WP(e,l?.config);if(!f&&!p){r.push(hI(t,`no Stripe payment method available`));return}let m=!!(f&&!l?.config.plugins?.includes(f)&&n.isStripeTestKey),h=e.request,g=wI(h.amount)?BigInt(h.amount):void 0,_=h.decimals??2,v=h.currency,y=g?cL(g,_):`unknown amount`,b=v?.length===3?v.toUpperCase():``,x=`${y}${b?` ${b}`:``} via Stripe${m?` (testmode)`:``}`;if(m)u.silent||console.log(X.dim(` Attempting: ${x}`));else if(!u.yes&&!n.isInteractive){r.push(hI(t,`non-interactive mode, use --yes to approve`));return}else if(u.yes)u.silent||console.log(X.dim(` Auto-approved: ${x}`));else if(!await FO(` ${X.yellow(`Pay`)} ${x}. Continue?`,!1)){r.push(hI(t,`declined`));return}let S,C,w;if(f)try{let t={paymentMethod:`pm_card_visa`};d&&(t.secretKey=d);let n=await f.setup({challenge:e,options:{},methodOpts:t});S=n.methods,C=n.createCredential,w=n.credentialContext}catch(e){r.push(hI(t,e.message));return}else S=[p];let T=await pL(e,S,C,t,r,l?.config.extensions,w);if(T){if(f?.prepareCredentialRequest?.({challenge:e,credential:T,headers:o}),m){let n=await bI(i,{method:a.method,headers:{...o,[fS(e)]:T},body:s??null},3e4);n.status>=200&&n.status<300?r.push(Q(`${t}: successful`,`HTTP ${n.status}`)):r.push(hI(`${t}: server is in livemode`,void 0,`Run your server with a Stripe test key to automatically validate Stripe payments in testmode.`));return}await mL(r,i,a,T,o,s,c,void 0,fS(e))}}async function pL(e,t,n,r,i,a,o){let s=new Response(null,{status:402,headers:{[q.wwwAuthenticate]:$x(e)}});try{let c=await UP(e,a,o),l;return l=n?await n(s,c):await wT({methods:t,polyfill:!1}).createCredential(s,c),i.push(Q(`${r}: submitted`)),l}catch(e){let t=e.message;t.toLowerCase().includes(`insufficient`)?i.push(hI(r,`insufficient balance: ${t}`)):i.push($(r,t));return}}async function mL(e,t,n,r,i,a,o,s,c=q.authorization){let l;try{l=await bI(t,{method:n.method,headers:{...i,[c]:r},body:a??null},3e4)}catch(t){return e.push($(`Payment: send credential`,t.message)),e}return hL(e,l,o,s)}async function hL(e,t,n,r){if(t.status===402){let n=await t.text().catch(()=>``),r=`Payment rejected`;try{let e=JSON.parse(n);r=e.detail??e.title??r}catch{}return e.push($(`Payment: accepted`,r,`The server rejected a valid credential. Check that your payment verification logic accepts the credential format and that the payment was processed on-chain.`)),e}if(t.status>=400&&t.status<500)e.push(mI(`Payment: post-payment response`,`Got ${t.status}`,`Payment succeeded but the endpoint returned a client error. The endpoint likely requires request body parameters. Use --body to provide them.`));else if(t.status>=500)return e.push($(`Payment: server response`,`Got ${t.status}`,`Payment was accepted but the server errored while generating the response. Check server logs for the underlying error.`)),e;else e.push(Q(`Payment: successful`,`HTTP ${t.status}`));let i=t.headers.get(q.paymentReceipt);if(!i)e.push($(`Payment-Receipt header present`,void 0,`After accepting payment, include a Payment-Receipt header with a base64url-encoded JSON object containing: method, reference, status ("success"), and timestamp (ISO 8601).`));else{e.push(Q(`Payment-Receipt header present`));try{let t=tL(i);if(e.push(Q(`Receipt parseable`)),t.status===`success`?e.push(Q(`Receipt status is "success"`)):e.push($(`Receipt status is "success"`,`Got: ${t.status}`)),t.reference){let n=/^0x[0-9a-fA-F]{64}$/.test(t.reference),r=t.reference.startsWith(`pi_`);n||r?e.push(Q(`Receipt reference valid`,t.reference.slice(0,20)+`...`)):e.push(mI(`Receipt reference format`,t.reference.slice(0,40)))}else e.push(mI(`Receipt has reference`,`No reference field`));if(t.timestamp){let n=new Date(t.timestamp),r=Date.now()-n.getTime();r<6e4?e.push(Q(`Receipt timestamp recent`,`${Math.round(r/1e3)}s ago`)):e.push(mI(`Receipt timestamp recent`,`${Math.round(r/6e4)}m ago`))}n&&console.log(X.dim(` Receipt: ${JSON.stringify(t,null,2)}`))}catch(t){e.push($(`Receipt parseable`,t.message))}}let a=t.headers.get(`content-type`)??``,o=await t.text().catch(()=>``);if(o.length>0)e.push(Q(`Response body non-empty`,`${a.split(`;`)[0]}, ${xI(o.length)}`));else{let n=[...t.headers.entries()].filter(([e])=>!e.startsWith(`x-`)&&![`content-type`,`content-length`,`date`,`server`,`connection`,`keep-alive`,`cache-control`,`vary`,`access-control-allow-origin`,`payment-receipt`,`payment-session`,`payment-session-snapshot`].includes(e.toLowerCase()));n.length>0?e.push(mI(`Response body empty -- data may be in headers only`,n.map(([e])=>e).join(`, `))):e.push(mI(`Response body empty`))}if(a?e.push(Q(`Content-Type header set`,a.split(`;`)[0])):e.push(mI(`Content-Type header set`)),i)try{let t=tL(i);if(t.reference&&/^0x[0-9a-fA-F]{64}$/.test(t.reference)){let n=r?.blockExplorers?.default?.url;if(n){let r=n.includes(`tempo`)?`receipt`:`tx`;e.push(Q(`On-chain transaction`,`${n}/${r}/${t.reference}`))}}}catch{}return e}async function*gL(e){let t=e.url.replace(/\/$/,``).replace(/\/openapi\.json$/i,``),n=e.verbose??!1,r=await xL(t,e);yield{phase:`discovery`,discovery:r,results:r.checks};for(let i of r.endpoints){yield{phase:`endpoint`,endpoint:i};let a,o=!1;e.endpoint?a=e.body:(a=EI(e.body,i.path),!a&&r.doc&&(a=jI(r.doc,i),o=!!a));let{results:s,resolvedBody:c,challenges:l}=await BI(t,i,n,{body:a,bodyIsAutogenerated:o,query:e.query,extraHeaders:e.headers,discoveryDoc:r.doc??void 0}),u=c??a,d=s.some(e=>e.severity===`pass`&&e.label===`Challenge parseable`),f=!s.some(e=>e.severity===`fail`),p=(l??[]).filter(e=>e.method!==`stripe`).map(e=>e.request.methodDetails?.chainId).filter(e=>typeof e==`number`),m=p.some(bL),h=p.some(e=>!bL(e)),g=!d&&s.some(e=>e.label===`Not an MPP endpoint`),_=s.some(e=>e.severity===`pass`&&e.label===`WWW-Authenticate header present`),v=s.some(e=>e.severity===`fail`&&e.label===`Challenge parseable`);if(yield{phase:`challenge`,endpoint:i,results:s,isMpp:d,isTestnet:m,isMainnet:h,isNonMppPayment:g,isMalformedChallenge:!d&&_&&v,methods:l?.map(e=>e.method)??[]},d&&(yield{phase:`errorHandling`,endpoint:i,results:await XI(t,i,{body:u,query:e.query})},!e.skipPayment&&f)){let r=e.onPaymentResults,a=await uL(t,i,n,{body:u,query:e.query,extraHeaders:e.headers,yes:e.yes,silent:!r,interactive:e.interactive,...r&&{onResults:r}});yield{phase:`payment`,endpoint:i,results:a,succeeded:a.some(e=>e.severity===`pass`&&e.label===`Payment: successful`)}}}}async function _L(e){let t=e.url.replace(/\/$/,``).replace(/\/openapi\.json$/i,``),n,r=[],i,a=!1,o=!1;for await(let t of gL(e))switch(t.phase){case`discovery`:n=t.discovery;break;case`endpoint`:i={method:t.endpoint.method,path:t.endpoint.path,challenge:[],errorHandling:[],payment:[]},r.push(i);break;case`challenge`:i&&(i.challenge=t.results),t.isMpp&&(t.isTestnet&&(a=!0),t.isMainnet&&(o=!0));break;case`errorHandling`:i&&(i.errorHandling=t.results);break;case`payment`:i&&(i.payment=t.results)}let s={passed:0,failed:0,warnings:0,suggested:0,skipped:0};if(n)for(let e of n.checks)e.severity===`pass`?s.passed++:e.severity===`fail`?s.failed++:e.severity===`warn`?s.warnings++:e.severity===`suggested`?s.suggested++:e.severity===`skip`&&s.skipped++;for(let e of r)for(let t of[...e.challenge,...e.errorHandling,...e.payment])t.severity===`pass`?s.passed++:t.severity===`fail`?s.failed++:t.severity===`warn`?s.warnings++:t.severity===`suggested`?s.suggested++:t.severity===`skip`&&s.skipped++;if(!n)throw Error(`Discovery phase did not complete`);let c=vL(r,n,{sawTestnet:a,sawMainnet:o});return{url:t,discovery:n,endpoints:r,summary:s,suggestions:c}}function vL(e,t,n){let r=[],i=e.flatMap(e=>[...e.challenge,...e.payment]);t.found||r.push(QI);let a=n.sawMainnet,o=n.sawTestnet,s=i.some(e=>e.label.includes(`[stripe]`)&&e.label.includes(`livemode`));return a&&!o?r.push(`Validate your testnet server to test without real funds (Tempo wallets are automatically provisioned).`):o&&!a&&r.push(`Validate your mainnet server to confirm real payments work end-to-end.`),s&&r.push(`Validate your testmode Stripe server to test card payments end-to-end (auto-pays with test card).`),r}const yL=Object.values(Be);function bL(e){return yL.find(t=>t.id===e)?.testnet===!0}async function xL(e,t){let n=[],r=[],i=null,a=!1,o=!1;n.push(await kI(e));let s=new URL(e).origin,c;t.discoveryPath?c=[new URL(t.discoveryPath,e+`/`).href.replace(/\/openapi\.json$/i,``)]:(c=[e,s,`${s}/api`],c=[...new Set(c)]);let l,u=[];for(let e of c)if(l=await OI(e),`error`in l)u.push(`${e}/openapi.json: ${l.error}`);else break;if(!l||`error`in l)n.push({label:`Document found`,detail:u.join(`; `),hint:`MPP servers must serve an OpenAPI document at /openapi.json with x-payment-info extensions.`,severity:`fail`});else{a=!0,n.push({label:`Document found and parseable`,severity:`pass`});let e=rE(l.doc),t=e.filter(e=>e.severity===`error`),r=e.filter(e=>e.severity===`warning`);if(t.length>0){n.push({label:`Valid OpenAPI structure`,detail:`${t.length} error(s)`,severity:`fail`});for(let e of t)n.push({label:e.message,detail:e.path,severity:`fail`})}else o=!0,n.push({label:`Valid OpenAPI structure`,severity:`pass`});for(let e of r)n.push({label:e.message,detail:e.path,severity:`warn`});i=l.doc}if(t.endpoint){let e=TI(t.endpoint);e&&r.push(e)}else if(i){r=AI(i);let e=BigInt(`999999999999999999`);r.sort((t,n)=>{let r=wI(t.amount)?BigInt(t.amount):e,i=wI(n.amount)?BigInt(n.amount):e;return r<i?-1:+(r>i)}),r.length>0?n.push({label:`Paid endpoints found`,detail:`${r.length} endpoint(s)`,severity:`pass`}):n.push({label:`Paid endpoints found`,detail:`No endpoints with x-payment-info`,severity:`warn`})}return{found:a,valid:o,endpoints:r,checks:n,doc:i}}Ox(),ZO();const SL=cb(`validate`,{description:`Validate an MPP server implementation end-to-end`,args:p.object({url:p.string().describe(`Base URL of the MPP server to validate`)}),options:p.object({endpoint:p.string().optional().describe(`Endpoint to test (METHOD:path). Skips discovery.`),body:p.string().optional().describe(`Request body. With --endpoint, used directly. In discovery mode, JSON with / keys is a per-path mapping.`),query:p.array(p.string()).optional().describe(`Query parameter (key=value, repeatable)`),header:p.array(p.string()).optional().describe(`Request header (key:value, repeatable)`),verbose:p.number().default(0).meta({count:!0}).describe(`Verbosity level`),yes:p.boolean().default(!1).describe(`Auto-approve mainnet payments`),outputJson:p.boolean().default(!1).describe(`Output results as JSON (auto-enabled in known agent environments)`)}),alias:{endpoint:`e`,header:`H`,verbose:`v`,yes:`y`,outputJson:`j`},async run(e){if(e.options.outputJson||MO()){let t=await _L({url:e.args.url,endpoint:e.options.endpoint,body:e.options.body,query:e.options.query,headers:e.options.header,verbose:e.options.verbose>0,yes:e.options.yes,interactive:!1});console.log(JSON.stringify(t,null,2));let n=t.endpoints.length===0&&!e.options.endpoint;(t.summary.failed>0||!t.discovery.found||n)&&process.exit(1);return}let t=e.args.url.replace(/\/$/,``).replace(/\/openapi\.json$/i,``);console.log(`\n${X.bold(`mppx validate`)} ${X.dim(t)}\n`);let n={passed:0,failed:0,warnings:0,suggested:0,skipped:0},r=!1,i=!1,a=!1,o=!1,s=!1,c=!1,l=!1,u=0;for await(let t of gL({url:e.args.url,endpoint:e.options.endpoint,body:e.options.body,query:e.options.query,headers:e.options.header,verbose:e.options.verbose>0,yes:e.options.yes,interactive:!!process.stdin.isTTY,onPaymentResults:e=>yI(e,n)}))switch(t.phase){case`discovery`:if(vI(`Discovery (/openapi.json)`),yI(t.results,n),l=t.discovery.found,!l&&!e.options.endpoint){console.log(``);let[e,...t]=QI.split(`
|
|
245
|
-
`);console.log(X.yellow(` ${e}`));for(let e of t)console.log(e?X.dim(` ${e}`):``);console.log(``),process.exit(1)}t.discovery.endpoints.length===0&&!e.options.endpoint&&l&&(console.log(X.dim(` Use --endpoint to specify endpoints manually.`)),console.log(``),process.exit(1));break;case`endpoint`:u++,vI(`${t.endpoint.method} ${t.endpoint.path}`);break;case`challenge`:console.log(X.dim(` Challenge`)),yI(t.results,n),t.isMpp&&(r=!0,t.isTestnet&&(o=!0),t.isMainnet&&(s=!0)),t.isNonMppPayment&&(i=!0),t.isMalformedChallenge&&(a=!0);break;case`errorHandling`:console.log(X.dim(` Error Handling`)),yI(t.results,n);break;case`payment`:t.succeeded&&(c=!0)}CL(n,{sawMppEndpoint:r,sawNonMppPaymentEndpoint:i,sawMalformedChallenge:a,sawTestnet:o,sawMainnet:s,paymentSucceeded:c},u)}});function CL(e,t,n){!t.sawMppEndpoint&&n>0&&(console.log(``),t.sawMalformedChallenge?(console.log(X.yellow(` Payment scheme detected but challenge format is invalid on ${n} endpoint(s).`)),console.log(X.dim(` The server uses WWW-Authenticate: Payment but the challenge parameters do not conform to MPP.`)),console.log(X.dim(` Fix: encode payment details as base64url JSON in a request="..." parameter. See errors above.`))):t.sawNonMppPaymentEndpoint?(console.log(X.yellow(` No MPP endpoints found. Tested ${n} endpoint(s) but none use WWW-Authenticate: Payment.`)),console.log(X.dim(` This server may use x402 or another payment protocol.`))):e.skipped>0&&e.failed===0?(console.log(X.yellow(` Could not reach payment gate on any endpoint (all returned 401/403/200).`)),console.log(X.dim(` The server may require authentication before payment. Try providing auth or use --endpoint with a public path.`))):(console.log(X.yellow(` No MPP endpoints found. Tested ${n} endpoint(s) but none use WWW-Authenticate: Payment.`)),console.log(X.dim(` This server may use x402 or another payment protocol.`))),console.log(``),process.exit(1)),console.log(``);let r=[];e.passed>0&&r.push(X.green(`${e.passed} passed`)),e.failed>0&&r.push(X.red(`${e.failed} failed`)),e.warnings>0&&r.push(X.yellow(`${e.warnings} warning(s)`)),e.suggested>0&&r.push(X.cyan(`${e.suggested} suggested`)),e.skipped>0&&r.push(X.yellow(`${e.skipped} skipped`)),console.log(`${X.bold(`Summary:`)} ${r.join(`, `)}`),t.paymentSucceeded&&t.sawTestnet&&!t.sawMainnet?(console.log(``),console.log(X.dim(` Tip: also validate your mainnet server to confirm real payments work end-to-end.`))):t.sawMainnet&&!t.sawTestnet&&(console.log(``),console.log(X.dim(` Tip: also validate your testnet server for free. This CLI automatically provisions and funds a testnet wallet for testing.`))),console.log(``),e.failed>0&&process.exit(1)}function wL(e){return EL(e)?e.offers:[e]}function TL(e){let{_canonicalRequest:t,intent:n,name:r}=e,i=t.methodDetails??{},a=DL(t.amount)??DL(i.amount)??null,o=DL(t.currency)??DL(i.currency),s=DL(e.description)??DL(t.description),c={amount:a,...o?{currency:o}:{},...s?{description:s}:{},intent:n,method:r},l=new Set([`amount`,`currency`,`description`,`methodDetails`]);for(let[e,n]of Object.entries(t))!l.has(e)&&n!==void 0&&(c[e]=n);return c}function EL(e){return`offers`in e&&!(`_canonicalRequest`in e)}function DL(e){return typeof e==`string`?e:void 0}var OL=I((()=>{})),kL=Ze({generate:()=>AL,generateProxy:()=>jL});function AL(e,t){let n=e.methods,r=new Map,i={};for(let e of n)r.set(`${e.name}/${e.intent}`,e),i[e.intent]=(i[e.intent]??0)+1;for(let e of n)i[e.intent]===1&&r.set(e.intent,e);let a=t.routes.map(e=>NL(e,r));return ML({info:{title:t.info?.title??e.realm,version:t.info?.version??`1.0.0`},routes:a,serviceInfo:t.serviceInfo})}function jL(e){let t=e.routes.map(t=>({...t,path:PL(e.basePath,t.path)}));return ML({info:{title:e.info?.title??`API Proxy`,version:e.info?.version??`1.0.0`},routes:t,serviceInfo:e.serviceInfo})}function ML(e){let t={};for(let n of e.routes){let e=n.method.toLowerCase(),r={responses:{...n.payment?{402:{description:`Payment Required`}}:{},200:{description:`Successful response`}}};n.payment&&(r[`x-payment-info`]=XT.parse(n.payment)),n.summary&&(r.summary=n.summary),n.requestBody&&(r.requestBody=n.requestBody),t[n.path]||(t[n.path]={}),t[n.path][e]=r}let n={info:e.info,openapi:`3.1.0`,paths:t};return e.serviceInfo&&(n[`x-service-info`]=e.serviceInfo),n}function NL(e,t){if(`handler`in e){let t=e.handler._internal;if(!t)throw Error(`Route ${e.method.toUpperCase()} ${e.path} is missing discovery metadata`);return{method:e.method,path:e.path,payment:{offers:wL(t).map(TL)},...e.requestBody?{requestBody:e.requestBody}:{},...e.summary?{summary:e.summary}:{}}}let n=t.get(e.intent);if(!n)throw Error(`Unknown intent "${e.intent}" for route ${e.method.toUpperCase()} ${e.path}. Available: ${[...t.keys()].join(`, `)}`);let{description:r,...i}=e.options,a={_canonicalRequest:Lx(n,i),...typeof r==`string`?{description:r}:{},intent:n.intent,name:n.name};return{method:e.method,path:e.path,payment:TL(a),...e.requestBody?{requestBody:e.requestBody}:{},...e.summary?{summary:e.summary}:{}}}function PL(e,t){if(!e)return t;let n=e.startsWith(`/`)?e:`/${e}`;return`${n.endsWith(`/`)?n.slice(0,-1):n}${t}`}var FL=I((()=>{zx(),nE(),OL()}));Ox(),_S(),yT(),JC(),sw(),uw(),Fx(),_O(),aw(),OS(),AC(),dk(),ZO();const IL=`0.14.0`,LL=p.object({address:p.string(),isDefault:p.boolean().optional(),name:p.string(),source:p.string().optional()}),RL=p.object({address:p.string(),balances:p.array(p.string()).optional(),name:p.string(),type:p.string().optional()}),zL=p.object({message:p.string(),path:p.string(),severity:p.string()}),BL=p.object({description:p.string().optional(),id:p.string(),name:p.string().optional(),paidEndpoints:p.number(),status:p.string().optional(),url:p.string().optional()}),VL=p.object({description:p.string().optional(),method:p.string(),path:p.string(),payment:p.unknown().optional()});function HL(e){return e.format===`json`&&e.formatExplicit}function UL(e,t,n){if(HL(e))return e.ok(t);n()}async function WL(t){if(!t.body)return;let n=t.body.getReader();try{for(;;){let{done:t,value:r}=await n.read();if(t)break;process.stdout.write(Buffer.from(r))||await e(process.stdout,`drain`)}}finally{n.releaseLock()}}function GL(){return process.stdin.isTTY===!1&&process.stdin.listenerCount(`data`)===0&&process.stdin.listenerCount(`readable`)===0}function KL(e){return typeof e==`string`?e:void 0}function qL(e){return Array.isArray(e.endpoints)?e.endpoints.filter(e=>typeof e==`object`&&!!e):[]}function JL(e){let t=qL(e);return{...KL(e.description)?{description:KL(e.description)}:{},id:KL(e.id)??KL(e.name)??`unknown`,...KL(e.name)?{name:KL(e.name)}:{},paidEndpoints:t.filter(e=>e.payment).length,...KL(e.status)?{status:KL(e.status)}:{},...KL(e.serviceUrl)||KL(e.url)?{url:KL(e.serviceUrl)??KL(e.url)}:{}}}function YL(e){return{...KL(e.description)?{description:KL(e.description)}:{},method:KL(e.method)??`GET`,path:KL(e.path)??`/`,...e.payment===void 0?{}:{payment:e.payment}}}function XL(e){if(!e)return`free`;if(typeof e!=`object`)return String(e);let t=e,n=KL(t.amount),r=KL(t.currency),i=KL(t.method),a=KL(t.intent);return[n,r,i&&a?`${i}/${a}`:i??a].filter(Boolean).join(` `)}function ZL(e){return e.request.currency??e.request.asset}function QL(e,t){if(!t)return[...e];let n=t.toLowerCase();return e.filter(e=>{let t=ZL(e);return typeof t==`string`&&t.toLowerCase()===n})}async function $L(e,t,n={}){let r=()=>qC().getChallenges(e),i=()=>ow().getChallenges(n.ignoreX402ResponseUrl?new Response(null,{headers:e.headers,status:e.status}):e);if(t===`mpp`)return r();if(t===`x402`)return i();let a=[],o;try{a=await r()}catch(e){o=e}let s=[],c;try{s=await i()}catch(e){c=e}if(a.length||s.length)return[...a,...s];if(o)throw o;if(c)throw c;return[]}function eR(e){return cw({name:`selected-challenge`,isPaymentRequired:e=>e.status===402,getChallenges:()=>[e],setCredential:e=>e})}function tR(e,t){if(!ES(e))return new Response(null,{status:402,headers:{[q.wwwAuthenticate]:$x(e)}});let n=e.request;return n.resource?new Response(null,{status:402,headers:{[fC]:nw({accepts:[sC(n)],...n.extensions&&{extensions:n.extensions},resource:n.resource,x402Version:2})}}):t}function nR(e){let{host:t,hostname:n}=new URL(e);return!n.endsWith(`.localhost`)||n===`localhost`?{url:e}:{host:t,url:e.replace(n,`127.0.0.1`)}}async function rR(e,t,n,r){let i=t,a=nR(i),o={...n,redirect:`manual`,headers:Mw(n.headers)};a.host&&oR(o.headers,a.host);for(let t=0;t<=20;t++){let n=await e(a.url,o),s=n.headers.get(`location`);if(!r||!s||![301,302,303,307,308].includes(n.status))return{init:o,logicalUrl:i,response:n,target:a};if(t===20)throw TypeError(`Too many redirects`);await n.body?.cancel().catch(()=>{});let c=new URL(s,i),l=new URL(i),u=Mw(o.headers);if(c.origin!==l.origin)for(let e of[`authorization`,`cookie`,`host`,`proxy-authorization`])for(let t of Object.keys(u))t.toLowerCase()===e&&delete u[t];let d=(o.method??(o.body?`POST`:`GET`)).toUpperCase(),f=n.status===303&&d!==`GET`&&d!==`HEAD`||(n.status===301||n.status===302)&&d===`POST`;if(f)for(let e of[`content-encoding`,`content-language`,`content-length`,`content-location`,`content-type`])for(let t of Object.keys(u))t.toLowerCase()===e&&delete u[t];i=c.href,a=nR(i),a.host&&oR(u,a.host);let p={...o};f&&(delete p.body,p.method=`GET`),o={...p,headers:u,redirect:`manual`}}throw TypeError(`Too many redirects`)}function iR(e,t,n){let r=new URL(e),i=new URL(n||e);return i.origin!==r.origin&&aR(i),nR(i.href)}function aR(e){throw new L({code:`UNSAFE_PAYMENT_REDIRECT`,message:`Refusing to send a payment credential to redirected origin ${e.origin}.`,exitCode:1})}function oR(e,t){for(let t of Object.keys(e))t.toLowerCase()===`host`&&delete e[t];t&&(e.Host=t)}function sR(e){let t=e.request.resource;return typeof t?.url==`string`?t.url:void 0}async function cR(){let e=process.env.MPPX_SERVICES_URL??`https://mpp.dev/api/services`,t=await globalThis.fetch(e);if(!t.ok)throw new L({code:`SERVICES_FETCH_FAILED`,message:`Failed to fetch services registry: HTTP ${t.status}`,exitCode:1});let n=await t.json();if(!n||typeof n!=`object`||!Array.isArray(n.services))throw new L({code:`SERVICES_INVALID`,message:`Services registry response did not contain a services array.`,exitCode:1});return n.services}function lR(e,t){let n=t.toLowerCase();return e.find(e=>{let t=KL(e.id)?.toLowerCase(),r=KL(e.name)?.toLowerCase();return t===n||r===n})}const uR=cb(`mppx`,{mcp:{tools:{discovery:`direct`}},version:IL,description:`Make HTTP requests with automatic payment handling`,usage:[{suffix:`<url> [options]`}],args:p.object({url:p.string().describe(`URL to make request to`)}),options:p.object({account:p.string().optional().describe(`Account name (env: MPPX_ACCOUNT)`),autoSwap:p.boolean().optional().describe(`Auto-swap source tokens into payment currency`),config:p.string().optional().describe(`Path to config file`),confirm:p.boolean().optional().default(!1).describe(`Show confirmation prompts`),currency:p.string().optional().describe(`Payment currency/token address to select`),data:p.string().optional().describe(`Send request body (implies POST unless -X is set)`),fail:p.boolean().optional().describe(`Fail silently on HTTP errors (exit 22)`),header:p.array(p.string()).optional().describe(`Add header (repeatable)`),include:p.boolean().optional().describe(`Include response headers in output`),insecure:p.boolean().optional().describe(`Skip TLS certificate verification (true for localhost/.local)`),jsonBody:p.string().optional().describe(`Send JSON body (sets Content-Type and Accept, implies POST)`),location:p.boolean().optional().describe(`Follow redirects`),method:p.string().optional().describe(`HTTP method`),methodOpt:p.array(p.string()).optional().describe(`Method-specific option (key=value, repeatable)`),network:p.enum([`mainnet`,`testnet`]).optional().describe(`Tempo network`),payWith:p.string().optional().describe(`Source token for Tempo auto-swap`),protocol:p.enum([`auto`,`mpp`,`x402`]).default(`auto`).describe(`Payment protocol to use`),rpcUrl:p.string().optional().describe(`RPC endpoint, defaults to public RPC for chain (env: MPPX_RPC_URL)`),session:p.string().optional().default(`auto`).refine(e=>e===`auto`||e===`new`||/^0x[0-9a-fA-F]{64}$/.test(e),`Expected auto, new, or a 32-byte channel ID`).describe(`Session selection: auto, new, or channel ID`),silent:p.boolean().default(!1).describe(`Silent mode (suppress progress and info)`),slippage:p.number().optional().describe(`Tempo auto-swap max slippage percentage`),userAgent:p.string().optional().describe(`Set User-Agent header`),verbose:p.number().default(0).meta({count:!0}).describe(`Verbosity (-v details, -vv headers)`)}),alias:{account:`a`,config:`c`,data:`d`,fail:`f`,header:`H`,include:`i`,insecure:`k`,jsonBody:`J`,location:`L`,method:`X`,methodOpt:`M`,rpcUrl:`r`,silent:`s`,userAgent:`A`,verbose:`v`},examples:[{args:{url:`mpp.dev/api/ping/paid`},description:`Make a payment request`}],async run(e){let t=e.options.silent?e=>{}:e=>process.stderr.write(e),n=await YP(e.options.config);n&&e.options.verbose>=1&&t(`${X.dim(`Using config`)} ${X.blue(c.relative(process.cwd(),n.path))}\n`);let r=pk(),i={"User-Agent":e.options.userAgent??`mppx/${IL}${r?` AIAgent/${r}`:``}`};if(e.options.header)for(let t of e.options.header){let n=t.indexOf(`:`);if(n===-1)return e.error({code:`INVALID_HEADER`,message:`Invalid header format: ${t}`,exitCode:2});i[t.slice(0,n).trim()]=t.slice(n+1).trim()}let a=KP(n?.config);a&&!Object.keys(i).some(e=>e.toLowerCase()===`accept-payment`)&&(i[`Accept-Payment`]=a);let o=(()=>{let t=/^https?:\/\//.test(e.args.url),n=/^(localhost|.*\.localhost|127\.0\.0\.1|\[::1\])(:\d+)?/.test(e.args.url);return t?e.args.url:`${n?`http`:`https`}://${e.args.url}`})(),{hostname:s}=new URL(o),l=e.options.insecure||s===`localhost`||s.endsWith(`.localhost`)||s.endsWith(`.local`)?async(e,t)=>{let n=process.env.NODE_TLS_REJECT_UNAUTHORIZED;process.env.NODE_TLS_REJECT_UNAUTHORIZED=`0`;try{return await globalThis.fetch(e,t)}finally{n===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=n}}:globalThis.fetch,u=nR(o),d=u.url;u.host&&(i.Host=u.host);try{let r=jO(e.options.methodOpt),a=fF(),s={redirect:e.options.location?`follow`:`manual`};e.options.jsonBody?(s.body=e.options.jsonBody,i[`Content-Type`]??=`application/json`,i.Accept??=`application/json`):e.options.data&&(s.body=e.options.data),e.options.method?s.method=e.options.method.toUpperCase():s.body&&(s.method=`POST`),Object.keys(i).length>0&&(s.headers=i);let c={include:e.options.include??!1,verbose:e.options.verbose,silent:e.options.silent};e.options.verbose>=2&&TO(o,s,t);let f=await rR(l,o,s,e.options.location??!1),p=f.response;if(p.status!==402){if(e.options.session!==`auto`)return e.error({code:`UNSUPPORTED_SESSION`,message:`--session requires a tempo/session payment challenge.`,exitCode:2});if(e.options.fail&&p.status>=400)return e.error({code:`HTTP_ERROR`,message:`HTTP error ${p.status}`,exitCode:22});EO(p,c),await WL(p);return}let m=p.url?new URL(p.url):void 0,h=u.host!==void 0&&m?.hostname===`127.0.0.1`,g=await $L(p,e.options.protocol,{ignoreX402ResponseUrl:h});if(g.length===0)return e.error({code:`UNSUPPORTED_PROTOCOL`,message:e.options.protocol===`auto`?`Server did not offer a payment challenge.`:`Server did not offer an ${e.options.protocol} payment challenge.`,exitCode:2});let _=QL(g,e.options.currency);if(e.options.currency&&_.length===0){let t=g.map(ZL).filter(e=>typeof e==`string`).join(`, `);return e.error({code:`UNSUPPORTED_CURRENCY`,message:`Server did not offer payment currency ${e.options.currency}.${t?` Offered currencies: ${t}.`:``}`,exitCode:2})}let v=qP(n?.config)?.length?_:await oP(_,{options:{account:e.options.account,network:e.options.network,rpcUrl:e.options.rpcUrl}}),y=GP(v,n?.config);if(!y){let t=v.map(e=>`${e.method}/${e.intent}`).join(`, `);return e.error({code:`UNSUPPORTED_METHOD`,message:`Unsupported payment method. Server offers: ${t}. Add it to mppx.config.ts using defineConfig().`,exitCode:2})}let{challenge:b,plugin:x,method:S}=y,C=ES(b),w=C?void 0:iR(o,u,f.logicalUrl),T=tR(b,p),ee=b.request.currency??``,E=b.request.decimals??6,D,O;x&&(O=await x.setup({challenge:b,options:{account:e.options.account,autoSwap:e.options.autoSwap,network:e.options.network,payWith:e.options.payWith,rpcUrl:e.options.rpcUrl,slippage:e.options.slippage},methodOpts:r}),ee=O.tokenSymbol,E=O.tokenDecimals,D=O.explorerUrl);let A=!e.options.silent&&e.options.confirm,te=new Set;{EO(p,b.method===`tempo`&&b.intent===`session`&&e.options.include?{...c,include:!1,verbose:Math.max(c.verbose,2)}:c);let n=(()=>{let e=new Set([`id`,`request`]),t=[];for(let[n,r]of Object.entries(b))e.has(n)||r===void 0||t.push([n,kO(n,r)]);return t.sort(([e],[t])=>e.localeCompare(t))})(),r={tokenSymbol:ee,tokenDecimals:E,explorerUrl:D},i=(()=>{let e=new Set([`decimals`,`currency`,`methodDetails`]),t=[];for(let[n,i]of Object.entries(b.request))e.has(n)||i===void 0||t.push([n,DO(n,i,r)]);return t.sort(([e],[t])=>e.localeCompare(t))})(),a=(()=>{let e=b.request.methodDetails;if(!e)return[];let t=[];for(let[n,i]of Object.entries(e))if(i!==void 0){if(n===`memo`&&typeof i==`string`){let e=OO(i);t.push([n,e?`${e}\n${X.dim(i)}`:i])}else t.push([n,DO(n,i,r)])}return t.sort(([e],[t])=>e.localeCompare(t))})(),o=[[`Challenge`,n],[`Request`,i],...a.length?[[`Details`,a]]:[]];for(let[,e]of o)for(let[t]of e)te.add(t);let s=Math.max(...o.flatMap(([,e])=>e.map(([e])=>e.length))),l=` ${``.padEnd(s)} `;if(e.options.verbose>=1||A){t(`${X.bold(X.yellow(`Payment Required`))}\n`);for(let[e,n]of o){t(`${X.bold(e)}\n`);for(let[e,r]of n){let[n,...i]=r.split(`
|
|
245
|
+
`);console.log(X.yellow(` ${e}`));for(let e of t)console.log(e?X.dim(` ${e}`):``);console.log(``),process.exit(1)}t.discovery.endpoints.length===0&&!e.options.endpoint&&l&&(console.log(X.dim(` Use --endpoint to specify endpoints manually.`)),console.log(``),process.exit(1));break;case`endpoint`:u++,vI(`${t.endpoint.method} ${t.endpoint.path}`);break;case`challenge`:console.log(X.dim(` Challenge`)),yI(t.results,n),t.isMpp&&(r=!0,t.isTestnet&&(o=!0),t.isMainnet&&(s=!0)),t.isNonMppPayment&&(i=!0),t.isMalformedChallenge&&(a=!0);break;case`errorHandling`:console.log(X.dim(` Error Handling`)),yI(t.results,n);break;case`payment`:t.succeeded&&(c=!0)}CL(n,{sawMppEndpoint:r,sawNonMppPaymentEndpoint:i,sawMalformedChallenge:a,sawTestnet:o,sawMainnet:s,paymentSucceeded:c},u)}});function CL(e,t,n){!t.sawMppEndpoint&&n>0&&(console.log(``),t.sawMalformedChallenge?(console.log(X.yellow(` Payment scheme detected but challenge format is invalid on ${n} endpoint(s).`)),console.log(X.dim(` The server uses WWW-Authenticate: Payment but the challenge parameters do not conform to MPP.`)),console.log(X.dim(` Fix: encode payment details as base64url JSON in a request="..." parameter. See errors above.`))):t.sawNonMppPaymentEndpoint?(console.log(X.yellow(` No MPP endpoints found. Tested ${n} endpoint(s) but none use WWW-Authenticate: Payment.`)),console.log(X.dim(` This server may use x402 or another payment protocol.`))):e.skipped>0&&e.failed===0?(console.log(X.yellow(` Could not reach payment gate on any endpoint (all returned 401/403/200).`)),console.log(X.dim(` The server may require authentication before payment. Try providing auth or use --endpoint with a public path.`))):(console.log(X.yellow(` No MPP endpoints found. Tested ${n} endpoint(s) but none use WWW-Authenticate: Payment.`)),console.log(X.dim(` This server may use x402 or another payment protocol.`))),console.log(``),process.exit(1)),console.log(``);let r=[];e.passed>0&&r.push(X.green(`${e.passed} passed`)),e.failed>0&&r.push(X.red(`${e.failed} failed`)),e.warnings>0&&r.push(X.yellow(`${e.warnings} warning(s)`)),e.suggested>0&&r.push(X.cyan(`${e.suggested} suggested`)),e.skipped>0&&r.push(X.yellow(`${e.skipped} skipped`)),console.log(`${X.bold(`Summary:`)} ${r.join(`, `)}`),t.paymentSucceeded&&t.sawTestnet&&!t.sawMainnet?(console.log(``),console.log(X.dim(` Tip: also validate your mainnet server to confirm real payments work end-to-end.`))):t.sawMainnet&&!t.sawTestnet&&(console.log(``),console.log(X.dim(` Tip: also validate your testnet server for free. This CLI automatically provisions and funds a testnet wallet for testing.`))),console.log(``),e.failed>0&&process.exit(1)}function wL(e){return EL(e)?e.offers:[e]}function TL(e){let{_canonicalRequest:t,intent:n,name:r}=e,i=t.methodDetails??{},a=DL(t.amount)??DL(i.amount)??null,o=DL(t.currency)??DL(i.currency),s=DL(e.description)??DL(t.description),c={amount:a,...o?{currency:o}:{},...s?{description:s}:{},intent:n,method:r},l=new Set([`amount`,`currency`,`description`,`methodDetails`]);for(let[e,n]of Object.entries(t))!l.has(e)&&n!==void 0&&(c[e]=n);return c}function EL(e){return`offers`in e&&!(`_canonicalRequest`in e)}function DL(e){return typeof e==`string`?e:void 0}var OL=I((()=>{})),kL=Ze({generate:()=>AL,generateProxy:()=>jL});function AL(e,t){let n=e.methods,r=new Map,i={};for(let e of n)r.set(`${e.name}/${e.intent}`,e),i[e.intent]=(i[e.intent]??0)+1;for(let e of n)i[e.intent]===1&&r.set(e.intent,e);let a=t.routes.map(e=>NL(e,r));return ML({info:{title:t.info?.title??e.realm,version:t.info?.version??`1.0.0`},routes:a,serviceInfo:t.serviceInfo})}function jL(e){let t=e.routes.map(t=>({...t,path:PL(e.basePath,t.path)}));return ML({info:{title:e.info?.title??`API Proxy`,version:e.info?.version??`1.0.0`},routes:t,serviceInfo:e.serviceInfo})}function ML(e){let t={};for(let n of e.routes){let e=n.method.toLowerCase(),r={responses:{...n.payment?{402:{description:`Payment Required`}}:{},200:{description:`Successful response`}}};n.payment&&(r[`x-payment-info`]=XT.parse(n.payment)),n.summary&&(r.summary=n.summary),n.requestBody&&(r.requestBody=n.requestBody),t[n.path]||(t[n.path]={}),t[n.path][e]=r}let n={info:e.info,openapi:`3.1.0`,paths:t};return e.serviceInfo&&(n[`x-service-info`]=e.serviceInfo),n}function NL(e,t){if(`handler`in e){let t=e.handler._internal;if(!t)throw Error(`Route ${e.method.toUpperCase()} ${e.path} is missing discovery metadata`);return{method:e.method,path:e.path,payment:{offers:wL(t).map(TL)},...e.requestBody?{requestBody:e.requestBody}:{},...e.summary?{summary:e.summary}:{}}}let n=t.get(e.intent);if(!n)throw Error(`Unknown intent "${e.intent}" for route ${e.method.toUpperCase()} ${e.path}. Available: ${[...t.keys()].join(`, `)}`);let{description:r,...i}=e.options,a={_canonicalRequest:Lx(n,i),...typeof r==`string`?{description:r}:{},intent:n.intent,name:n.name};return{method:e.method,path:e.path,payment:TL(a),...e.requestBody?{requestBody:e.requestBody}:{},...e.summary?{summary:e.summary}:{}}}function PL(e,t){if(!e)return t;let n=e.startsWith(`/`)?e:`/${e}`;return`${n.endsWith(`/`)?n.slice(0,-1):n}${t}`}var FL=I((()=>{zx(),nE(),OL()}));Ox(),_S(),yT(),JC(),sw(),uw(),Fx(),_O(),aw(),OS(),AC(),dk(),ZO();const IL=`0.14.1`,LL=p.object({address:p.string(),isDefault:p.boolean().optional(),name:p.string(),source:p.string().optional()}),RL=p.object({address:p.string(),balances:p.array(p.string()).optional(),name:p.string(),type:p.string().optional()}),zL=p.object({message:p.string(),path:p.string(),severity:p.string()}),BL=p.object({description:p.string().optional(),id:p.string(),name:p.string().optional(),paidEndpoints:p.number(),status:p.string().optional(),url:p.string().optional()}),VL=p.object({description:p.string().optional(),method:p.string(),path:p.string(),payment:p.unknown().optional()});function HL(e){return e.format===`json`&&e.formatExplicit}function UL(e,t,n){if(HL(e))return e.ok(t);n()}async function WL(t){if(!t.body)return;let n=t.body.getReader();try{for(;;){let{done:t,value:r}=await n.read();if(t)break;process.stdout.write(Buffer.from(r))||await e(process.stdout,`drain`)}}finally{n.releaseLock()}}function GL(){return process.stdin.isTTY===!1&&process.stdin.listenerCount(`data`)===0&&process.stdin.listenerCount(`readable`)===0}function KL(e){return typeof e==`string`?e:void 0}function qL(e){return Array.isArray(e.endpoints)?e.endpoints.filter(e=>typeof e==`object`&&!!e):[]}function JL(e){let t=qL(e);return{...KL(e.description)?{description:KL(e.description)}:{},id:KL(e.id)??KL(e.name)??`unknown`,...KL(e.name)?{name:KL(e.name)}:{},paidEndpoints:t.filter(e=>e.payment).length,...KL(e.status)?{status:KL(e.status)}:{},...KL(e.serviceUrl)||KL(e.url)?{url:KL(e.serviceUrl)??KL(e.url)}:{}}}function YL(e){return{...KL(e.description)?{description:KL(e.description)}:{},method:KL(e.method)??`GET`,path:KL(e.path)??`/`,...e.payment===void 0?{}:{payment:e.payment}}}function XL(e){if(!e)return`free`;if(typeof e!=`object`)return String(e);let t=e,n=KL(t.amount),r=KL(t.currency),i=KL(t.method),a=KL(t.intent);return[n,r,i&&a?`${i}/${a}`:i??a].filter(Boolean).join(` `)}function ZL(e){return e.request.currency??e.request.asset}function QL(e,t){if(!t)return[...e];let n=t.toLowerCase();return e.filter(e=>{let t=ZL(e);return typeof t==`string`&&t.toLowerCase()===n})}async function $L(e,t,n={}){let r=()=>qC().getChallenges(e),i=()=>ow().getChallenges(n.ignoreX402ResponseUrl?new Response(null,{headers:e.headers,status:e.status}):e);if(t===`mpp`)return r();if(t===`x402`)return i();let a=[],o;try{a=await r()}catch(e){o=e}let s=[],c;try{s=await i()}catch(e){c=e}if(a.length||s.length)return[...a,...s];if(o)throw o;if(c)throw c;return[]}function eR(e){return cw({name:`selected-challenge`,isPaymentRequired:e=>e.status===402,getChallenges:()=>[e],setCredential:e=>e})}function tR(e,t){if(!ES(e))return new Response(null,{status:402,headers:{[q.wwwAuthenticate]:$x(e)}});let n=e.request;return n.resource?new Response(null,{status:402,headers:{[fC]:nw({accepts:[sC(n)],...n.extensions&&{extensions:n.extensions},resource:n.resource,x402Version:2})}}):t}function nR(e){let{host:t,hostname:n}=new URL(e);return!n.endsWith(`.localhost`)||n===`localhost`?{url:e}:{host:t,url:e.replace(n,`127.0.0.1`)}}async function rR(e,t,n,r){let i=t,a=nR(i),o={...n,redirect:`manual`,headers:Mw(n.headers)};a.host&&oR(o.headers,a.host);for(let t=0;t<=20;t++){let n=await e(a.url,o),s=n.headers.get(`location`);if(!r||!s||![301,302,303,307,308].includes(n.status))return{init:o,logicalUrl:i,response:n,target:a};if(t===20)throw TypeError(`Too many redirects`);await n.body?.cancel().catch(()=>{});let c=new URL(s,i),l=new URL(i),u=Mw(o.headers);if(c.origin!==l.origin)for(let e of[`authorization`,`cookie`,`host`,`proxy-authorization`])for(let t of Object.keys(u))t.toLowerCase()===e&&delete u[t];let d=(o.method??(o.body?`POST`:`GET`)).toUpperCase(),f=n.status===303&&d!==`GET`&&d!==`HEAD`||(n.status===301||n.status===302)&&d===`POST`;if(f)for(let e of[`content-encoding`,`content-language`,`content-length`,`content-location`,`content-type`])for(let t of Object.keys(u))t.toLowerCase()===e&&delete u[t];i=c.href,a=nR(i),a.host&&oR(u,a.host);let p={...o};f&&(delete p.body,p.method=`GET`),o={...p,headers:u,redirect:`manual`}}throw TypeError(`Too many redirects`)}function iR(e,t,n){let r=new URL(e),i=new URL(n||e);return i.origin!==r.origin&&aR(i),nR(i.href)}function aR(e){throw new L({code:`UNSAFE_PAYMENT_REDIRECT`,message:`Refusing to send a payment credential to redirected origin ${e.origin}.`,exitCode:1})}function oR(e,t){for(let t of Object.keys(e))t.toLowerCase()===`host`&&delete e[t];t&&(e.Host=t)}function sR(e){let t=e.request.resource;return typeof t?.url==`string`?t.url:void 0}async function cR(){let e=process.env.MPPX_SERVICES_URL??`https://mpp.dev/api/services`,t=await globalThis.fetch(e);if(!t.ok)throw new L({code:`SERVICES_FETCH_FAILED`,message:`Failed to fetch services registry: HTTP ${t.status}`,exitCode:1});let n=await t.json();if(!n||typeof n!=`object`||!Array.isArray(n.services))throw new L({code:`SERVICES_INVALID`,message:`Services registry response did not contain a services array.`,exitCode:1});return n.services}function lR(e,t){let n=t.toLowerCase();return e.find(e=>{let t=KL(e.id)?.toLowerCase(),r=KL(e.name)?.toLowerCase();return t===n||r===n})}const uR=cb(`mppx`,{mcp:{tools:{discovery:`direct`}},version:IL,description:`Make HTTP requests with automatic payment handling`,usage:[{suffix:`<url> [options]`}],args:p.object({url:p.string().describe(`URL to make request to`)}),options:p.object({account:p.string().optional().describe(`Account name (env: MPPX_ACCOUNT)`),autoSwap:p.boolean().optional().describe(`Auto-swap source tokens into payment currency`),config:p.string().optional().describe(`Path to config file`),confirm:p.boolean().optional().default(!1).describe(`Show confirmation prompts`),currency:p.string().optional().describe(`Payment currency/token address to select`),data:p.string().optional().describe(`Send request body (implies POST unless -X is set)`),fail:p.boolean().optional().describe(`Fail silently on HTTP errors (exit 22)`),header:p.array(p.string()).optional().describe(`Add header (repeatable)`),include:p.boolean().optional().describe(`Include response headers in output`),insecure:p.boolean().optional().describe(`Skip TLS certificate verification (true for localhost/.local)`),jsonBody:p.string().optional().describe(`Send JSON body (sets Content-Type and Accept, implies POST)`),location:p.boolean().optional().describe(`Follow redirects`),method:p.string().optional().describe(`HTTP method`),methodOpt:p.array(p.string()).optional().describe(`Method-specific option (key=value, repeatable)`),network:p.enum([`mainnet`,`testnet`]).optional().describe(`Tempo network`),payWith:p.string().optional().describe(`Source token for Tempo auto-swap`),protocol:p.enum([`auto`,`mpp`,`x402`]).default(`auto`).describe(`Payment protocol to use`),rpcUrl:p.string().optional().describe(`RPC endpoint, defaults to public RPC for chain (env: MPPX_RPC_URL)`),session:p.string().optional().default(`auto`).refine(e=>e===`auto`||e===`new`||/^0x[0-9a-fA-F]{64}$/.test(e),`Expected auto, new, or a 32-byte channel ID`).describe(`Session selection: auto, new, or channel ID`),silent:p.boolean().default(!1).describe(`Silent mode (suppress progress and info)`),slippage:p.number().optional().describe(`Tempo auto-swap max slippage percentage`),userAgent:p.string().optional().describe(`Set User-Agent header`),verbose:p.number().default(0).meta({count:!0}).describe(`Verbosity (-v details, -vv headers)`)}),alias:{account:`a`,config:`c`,data:`d`,fail:`f`,header:`H`,include:`i`,insecure:`k`,jsonBody:`J`,location:`L`,method:`X`,methodOpt:`M`,rpcUrl:`r`,silent:`s`,userAgent:`A`,verbose:`v`},examples:[{args:{url:`mpp.dev/api/ping/paid`},description:`Make a payment request`}],async run(e){let t=e.options.silent?e=>{}:e=>process.stderr.write(e),n=await YP(e.options.config);n&&e.options.verbose>=1&&t(`${X.dim(`Using config`)} ${X.blue(c.relative(process.cwd(),n.path))}\n`);let r=pk(),i={"User-Agent":e.options.userAgent??`mppx/${IL}${r?` AIAgent/${r}`:``}`};if(e.options.header)for(let t of e.options.header){let n=t.indexOf(`:`);if(n===-1)return e.error({code:`INVALID_HEADER`,message:`Invalid header format: ${t}`,exitCode:2});i[t.slice(0,n).trim()]=t.slice(n+1).trim()}let a=KP(n?.config);a&&!Object.keys(i).some(e=>e.toLowerCase()===`accept-payment`)&&(i[`Accept-Payment`]=a);let o=(()=>{let t=/^https?:\/\//.test(e.args.url),n=/^(localhost|.*\.localhost|127\.0\.0\.1|\[::1\])(:\d+)?/.test(e.args.url);return t?e.args.url:`${n?`http`:`https`}://${e.args.url}`})(),{hostname:s}=new URL(o),l=e.options.insecure||s===`localhost`||s.endsWith(`.localhost`)||s.endsWith(`.local`)?async(e,t)=>{let n=process.env.NODE_TLS_REJECT_UNAUTHORIZED;process.env.NODE_TLS_REJECT_UNAUTHORIZED=`0`;try{return await globalThis.fetch(e,t)}finally{n===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=n}}:globalThis.fetch,u=nR(o),d=u.url;u.host&&(i.Host=u.host);try{let r=jO(e.options.methodOpt),a=fF(),s={redirect:e.options.location?`follow`:`manual`};e.options.jsonBody?(s.body=e.options.jsonBody,i[`Content-Type`]??=`application/json`,i.Accept??=`application/json`):e.options.data&&(s.body=e.options.data),e.options.method?s.method=e.options.method.toUpperCase():s.body&&(s.method=`POST`),Object.keys(i).length>0&&(s.headers=i);let c={include:e.options.include??!1,verbose:e.options.verbose,silent:e.options.silent};e.options.verbose>=2&&TO(o,s,t);let f=await rR(l,o,s,e.options.location??!1),p=f.response;if(p.status!==402){if(e.options.session!==`auto`)return e.error({code:`UNSUPPORTED_SESSION`,message:`--session requires a tempo/session payment challenge.`,exitCode:2});if(e.options.fail&&p.status>=400)return e.error({code:`HTTP_ERROR`,message:`HTTP error ${p.status}`,exitCode:22});EO(p,c),await WL(p);return}let m=p.url?new URL(p.url):void 0,h=u.host!==void 0&&m?.hostname===`127.0.0.1`,g=await $L(p,e.options.protocol,{ignoreX402ResponseUrl:h});if(g.length===0)return e.error({code:`UNSUPPORTED_PROTOCOL`,message:e.options.protocol===`auto`?`Server did not offer a payment challenge.`:`Server did not offer an ${e.options.protocol} payment challenge.`,exitCode:2});let _=QL(g,e.options.currency);if(e.options.currency&&_.length===0){let t=g.map(ZL).filter(e=>typeof e==`string`).join(`, `);return e.error({code:`UNSUPPORTED_CURRENCY`,message:`Server did not offer payment currency ${e.options.currency}.${t?` Offered currencies: ${t}.`:``}`,exitCode:2})}let v=qP(n?.config)?.length?_:await oP(_,{options:{account:e.options.account,network:e.options.network,rpcUrl:e.options.rpcUrl}}),y=GP(v,n?.config);if(!y){let t=v.map(e=>`${e.method}/${e.intent}`).join(`, `);return e.error({code:`UNSUPPORTED_METHOD`,message:`Unsupported payment method. Server offers: ${t}. Add it to mppx.config.ts using defineConfig().`,exitCode:2})}let{challenge:b,plugin:x,method:S}=y,C=ES(b),w=C?void 0:iR(o,u,f.logicalUrl),T=tR(b,p),ee=b.request.currency??``,E=b.request.decimals??6,D,O;x&&(O=await x.setup({challenge:b,options:{account:e.options.account,autoSwap:e.options.autoSwap,network:e.options.network,payWith:e.options.payWith,rpcUrl:e.options.rpcUrl,slippage:e.options.slippage},methodOpts:r}),ee=O.tokenSymbol,E=O.tokenDecimals,D=O.explorerUrl);let A=!e.options.silent&&e.options.confirm,te=new Set;{EO(p,b.method===`tempo`&&b.intent===`session`&&e.options.include?{...c,include:!1,verbose:Math.max(c.verbose,2)}:c);let n=(()=>{let e=new Set([`id`,`request`]),t=[];for(let[n,r]of Object.entries(b))e.has(n)||r===void 0||t.push([n,kO(n,r)]);return t.sort(([e],[t])=>e.localeCompare(t))})(),r={tokenSymbol:ee,tokenDecimals:E,explorerUrl:D},i=(()=>{let e=new Set([`decimals`,`currency`,`methodDetails`]),t=[];for(let[n,i]of Object.entries(b.request))e.has(n)||i===void 0||t.push([n,DO(n,i,r)]);return t.sort(([e],[t])=>e.localeCompare(t))})(),a=(()=>{let e=b.request.methodDetails;if(!e)return[];let t=[];for(let[n,i]of Object.entries(e))if(i!==void 0){if(n===`memo`&&typeof i==`string`){let e=OO(i);t.push([n,e?`${e}\n${X.dim(i)}`:i])}else t.push([n,DO(n,i,r)])}return t.sort(([e],[t])=>e.localeCompare(t))})(),o=[[`Challenge`,n],[`Request`,i],...a.length?[[`Details`,a]]:[]];for(let[,e]of o)for(let[t]of e)te.add(t);let s=Math.max(...o.flatMap(([,e])=>e.map(([e])=>e.length))),l=` ${``.padEnd(s)} `;if(e.options.verbose>=1||A){t(`${X.bold(X.yellow(`Payment Required`))}\n`);for(let[e,n]of o){t(`${X.bold(e)}\n`);for(let[e,r]of n){let[n,...i]=r.split(`
|
|
246
246
|
`);t(` ${X.dim(e.padEnd(s))} ${n}\n`);for(let e of i)t(`${l}${e}\n`)}}}if(A&&(t(`
|
|
247
247
|
`),!await FO(`Proceed with ${b.intent}?`,!0))){t(`Aborted.
|
|
248
248
|
`);return}}let ne=process.env.MPPX_PRIVATE_KEY?.trim()||!NO(ik(e.options.account));if(FD(b)&&ne&&!S){try{await pI({challenge:b,challengeResponse:T,credentialContext:await UP(b,n?.config.extensions,O?.credentialContext),endpoint:f.logicalUrl,fetch:l,fetchInput:w.url,init:{...f.init,redirect:`manual`},info:t,methodOptions:r,options:{account:e.options.account,fail:e.options.fail,include:e.options.include,network:e.options.network,rpcUrl:e.options.rpcUrl,session:e.options.session,silent:e.options.silent,verbose:e.options.verbose},registry:a})}catch(e){return nI(e,`SESSION_REQUEST_FAILED`)}return}if(e.options.session!==`auto`)return e.error({code:`UNSUPPORTED_SESSION`,message:S&&FD(b)?`--session cannot override a configured session method. Configure its channelStore instead.`:`--session requires a tempo/session payment challenge.`,exitCode:2});let j=S&&FD(b)?void 0:await UP(b,n?.config.extensions,O?.credentialContext),M,re;if(S&&FD(b)){let e=T;M=await wT({methods:[S],polyfill:!1,async onChallenge(e,{createCredential:t}){let r=await UP(e,n?.config.extensions,O?.credentialContext);return ZP(b,e),t(r)},fetch:async(t,n)=>{if(e){let t=e;return e=void 0,t}return l(t,n)}}).fetch(w.url,{...f.init,context:j,redirect:`manual`})}else{if(O?.createCredential)re=await O.createCredential(T,j);else if(O)re=await wT({methods:O.methods,polyfill:!1,transport:eR(b)}).createCredential(T,j);else if(S)re=await wT({methods:[S],polyfill:!1,transport:eR(b)}).createCredential(T,j);else throw Error(`unreachable`);let n=C?pC:fS(b),r={...Mw(f.init.headers),[n]:re},i=C?nR((sR(b)??p.url)||o):w;C&&oR(r,i.host),x?.prepareCredentialRequest?.({challenge:b,credential:re,headers:r});let a={...f.init,redirect:`manual`,headers:r};e.options.verbose>=2&&TO(i.url,a,t),M=await l(i.url,a)}if(e.options.fail&&M.status>=400)return e.error({code:`HTTP_ERROR`,message:`HTTP error ${M.status}`,exitCode:22});if(M.status===402){let n=await M.text();t(`${X.bold(X.red(`Payment Rejected`))}\n`);try{let e=JSON.parse(n),r=[];for(let[t,n]of Object.entries(e))n!==void 0&&r.push([t,String(n)]);r.sort(([e],[t])=>e.localeCompare(t));let i=Math.max(...r.map(([e])=>e.length));for(let[e,n]of r)t(` ${X.dim(e.padEnd(i))} ${n}\n`)}catch{n&&t(` ${n}\n`)}return e.error({code:`PAYMENT_REJECTED`,message:`Payment rejected`,exitCode:75})}if(EO(M,c),!(re&&await x?.handleResponse?.({challenge:b,credential:re,response:M,fetchUrl:d,fetchInit:s,silent:e.options.silent,verbose:e.options.verbose,confirmEnabled:A,confirm:FO,tokenSymbol:ee,tokenDecimals:E,explorerUrl:D,shownKeys:te}))){let n=M.headers.get(q.paymentReceipt);if(n&&e.options.verbose>=1)try{let e=JSON.parse(k.toString(n));t(`\n${X.bold(X.green(`Payment Receipt`))}\n`);let r=[],i=e.channelId,a=e.reference,o=i&&a&&i===a,s=new Set([`acceptedCumulative`,`spent`]);for(let[t,n]of Object.entries(e)){if(n===void 0||te.has(t)||t===`reference`&&o)continue;let e=x?.formatReceiptField?.(t,n);e===void 0?s.has(t)&&typeof n==`string`?r.push([t,`${n} ${X.dim(`(${IO(BigInt(n),ee,E)})`)}`]):(t===`reference`||t===`txHash`)&&typeof n==`string`&&D?r.push([t,AO(`${D}/tx/${n}`,n)]):r.push([t,String(n)]):r.push([t,e])}r.sort(([e],[t])=>e.localeCompare(t));let c=Math.max(...r.map(([e])=>e.length));for(let[e,n]of r)t(` ${X.dim(e.padEnd(c))} ${n}\n`);t(`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Current mppx package version. */
|
|
2
|
-
export declare const version = "0.14.
|
|
2
|
+
export declare const version = "0.14.1";
|
|
3
3
|
/** Canonical SDK identifier for this mppx release. */
|
|
4
|
-
export declare const sdkIdentifier = "mppx/0.14.
|
|
4
|
+
export declare const sdkIdentifier = "mppx/0.14.1";
|
|
5
5
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/internal/version.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by scripts/sync-version.ts.
|
|
2
2
|
/** Current mppx package version. */
|
|
3
|
-
export const version = '0.14.
|
|
3
|
+
export const version = '0.14.1';
|
|
4
4
|
/** Canonical SDK identifier for this mppx release. */
|
|
5
5
|
export const sdkIdentifier = `mppx/${version}`;
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Hex } from 'viem';
|
|
2
2
|
import * as Chain from '../precompile/Chain.js';
|
|
3
|
-
import { type OnSessionSettlementFailure, type SessionStoreInput, type SettlementTransactionOptions } from './Settlement.js';
|
|
3
|
+
import { type OnSessionSettlementFailure, type ResolvedSettlementSchedule, type SessionStoreInput, type SettlementTransactionOptions } from './Settlement.js';
|
|
4
4
|
/** Channels whose escrow states one call reads, bounding its calldata and response size. */
|
|
5
5
|
export declare const defaultBatchSize = 50;
|
|
6
6
|
/** Widest log query `rpc.tempo.xyz` accepts; wider ones fail with "query exceeds max block range". */
|
|
@@ -15,6 +15,8 @@ export declare const defaultCloseSearchMarginBlocks = 2000n;
|
|
|
15
15
|
export type CloseSupervisionOutcome =
|
|
16
16
|
/** Open with no close request, or not yet open at the pinned block. */
|
|
17
17
|
'open'
|
|
18
|
+
/** Open, and settled because its spend crossed `settlementSchedule`. */
|
|
19
|
+
| 'settled'
|
|
18
20
|
/** Close requested, with nothing above the settled amount to capture; the payer's withdrawal finalizes it. */
|
|
19
21
|
| 'closing'
|
|
20
22
|
/** A close request recorded on the channel was cancelled on-chain, so the channel accepts charges again. */
|
|
@@ -45,6 +47,8 @@ export type CloseSupervision = {
|
|
|
45
47
|
closed: number;
|
|
46
48
|
/** Channels that failed. */
|
|
47
49
|
failed: number;
|
|
50
|
+
/** Open channels settled on `settlementSchedule`. */
|
|
51
|
+
settled: number;
|
|
48
52
|
/** Channels whose deposit left the escrow before their spend was captured. */
|
|
49
53
|
unrecovered: number;
|
|
50
54
|
/**
|
|
@@ -78,6 +82,14 @@ export type SuperviseClosesParameters = Pick<SettlementTransactionOptions, 'acco
|
|
|
78
82
|
onSessionSettlementFailure?: OnSessionSettlementFailure | undefined;
|
|
79
83
|
/** `block` of the last `complete` supervision, which bounds close-event searches. */
|
|
80
84
|
previousBlock?: bigint | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Settles open channels whose spend since their last settlement crossed this schedule, in raw
|
|
87
|
+
* token units, with the account and fee options of closes. Servers that charge channels with
|
|
88
|
+
* `charge()` rather than through the session handler, whose own schedule runs on each
|
|
89
|
+
* charge, use it to collect spend without waiting for a close. Settlement captures only a voucher
|
|
90
|
+
* within spend. Without it, open channels are only read.
|
|
91
|
+
*/
|
|
92
|
+
settlementSchedule?: ResolvedSettlementSchedule | undefined;
|
|
81
93
|
/** Server channel store. */
|
|
82
94
|
store: SessionStoreInput;
|
|
83
95
|
};
|
|
@@ -4,7 +4,7 @@ import { VerificationFailedError } from '../../../Errors.js';
|
|
|
4
4
|
import * as Chain from '../precompile/Chain.js';
|
|
5
5
|
import { escrowAbi } from '../precompile/escrow.abi.js';
|
|
6
6
|
import * as ChannelStore from './ChannelStore.js';
|
|
7
|
-
import { assertSettlementSender, getClientAccount, reportSessionSettlementFailure, resolveChannelStore, } from './Settlement.js';
|
|
7
|
+
import { assertSettlementSender, getClientAccount, maybeSettleScheduled, reportSessionSettlementFailure, resolveChannelStore, } from './Settlement.js';
|
|
8
8
|
/** Escrow event every close and withdrawal emits, with the amount paid to the payee. */
|
|
9
9
|
const channelClosedEvent = getAbiItem({ abi: escrowAbi, name: 'ChannelClosed' });
|
|
10
10
|
/** Channels whose escrow states one call reads, bounding its calldata and response size. */
|
|
@@ -45,6 +45,7 @@ export async function superviseCloses(parameters) {
|
|
|
45
45
|
closed: 0,
|
|
46
46
|
complete: false,
|
|
47
47
|
failed: 0,
|
|
48
|
+
settled: 0,
|
|
48
49
|
supervised: 0,
|
|
49
50
|
timestamp: pinned.timestamp,
|
|
50
51
|
unrecovered: 0,
|
|
@@ -54,6 +55,7 @@ export async function superviseCloses(parameters) {
|
|
|
54
55
|
if (outcome === 'cancelled' ||
|
|
55
56
|
outcome === 'closed' ||
|
|
56
57
|
outcome === 'failed' ||
|
|
58
|
+
outcome === 'settled' ||
|
|
57
59
|
outcome === 'unrecovered')
|
|
58
60
|
supervision[outcome] += 1;
|
|
59
61
|
supervision.channels.push({ channelId, outcome, ...details });
|
|
@@ -172,7 +174,7 @@ async function superviseChannel(parameters, store, channel, state, blocks) {
|
|
|
172
174
|
: current);
|
|
173
175
|
if (state.closeRequestedAt === 0) {
|
|
174
176
|
if (channel.closeRequestedAt === 0n)
|
|
175
|
-
return
|
|
177
|
+
return settleDue(parameters, store, channel);
|
|
176
178
|
// A pinned read without a request means the payer cancelled it, unless the record holds a
|
|
177
179
|
// server close that may still land: those expire before they are cleared.
|
|
178
180
|
const cleared = {};
|
|
@@ -198,6 +200,25 @@ async function superviseChannel(parameters, store, channel, state, blocks) {
|
|
|
198
200
|
return { outcome: 'finalized' };
|
|
199
201
|
return closeAtSpend(parameters, store, closing, state);
|
|
200
202
|
}
|
|
203
|
+
/** Settles an open channel when its spend crossed the settlement schedule. */
|
|
204
|
+
async function settleDue(parameters, store, channel) {
|
|
205
|
+
const { client, settlementSchedule } = parameters;
|
|
206
|
+
if (!settlementSchedule)
|
|
207
|
+
return { outcome: 'open' };
|
|
208
|
+
const txHash = await maybeSettleScheduled({
|
|
209
|
+
account: parameters.account ?? getClientAccount(client),
|
|
210
|
+
channel,
|
|
211
|
+
client,
|
|
212
|
+
...(parameters.feePayer ? { feePayer: parameters.feePayer } : {}),
|
|
213
|
+
...(parameters.feePayerPolicy ? { feePayerPolicy: parameters.feePayerPolicy } : {}),
|
|
214
|
+
...(parameters.feeToken ? { feeToken: parameters.feeToken } : {}),
|
|
215
|
+
onSessionSettlement: parameters.onSessionSettlement,
|
|
216
|
+
onSessionSettlementFailure: parameters.onSessionSettlementFailure,
|
|
217
|
+
schedule: settlementSchedule,
|
|
218
|
+
store,
|
|
219
|
+
});
|
|
220
|
+
return txHash ? { outcome: 'settled', txHash } : { outcome: 'open' };
|
|
221
|
+
}
|
|
201
222
|
/** Sends a payee close capturing the channel's spend and finalizes the channel. */
|
|
202
223
|
async function closeAtSpend(parameters, store, channel, state) {
|
|
203
224
|
const { client } = parameters;
|
|
@@ -9,6 +9,8 @@ export type { ChargeOptions, CreditDeclineReason, CreditOptions, CreditResult, S
|
|
|
9
9
|
export type { Parameters as SponsorshipLimitParameters } from './SponsorshipLimit.js';
|
|
10
10
|
/** SSE helpers and types for Tempo session streams. */
|
|
11
11
|
export * as Sse from './Sse.js';
|
|
12
|
-
/** Server-side automatic settlement schedule. */
|
|
12
|
+
/** Server-side automatic settlement schedule, and its raw-unit form `superviseCloses` takes. */
|
|
13
|
+
export { resolveSettlementSchedule } from './Settlement.js';
|
|
14
|
+
export type { ResolvedSettlementSchedule } from './Settlement.js';
|
|
13
15
|
export type { OnSessionSettlement, OnSessionSettlementFailure, ResolveSessionChannelId, ResolveSessionChannelIdParameters, SessionChannelIdRequest, SessionSettlementContext, SessionSettlementFailureContext, SettlementSchedule, } from './Session.js';
|
|
14
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -5,4 +5,6 @@ export { superviseCloses } from './CloseSupervisor.js';
|
|
|
5
5
|
export { IdempotencyKeyConflictError, resolveSettleableVoucher } from './ChannelStore.js';
|
|
6
6
|
/** SSE helpers and types for Tempo session streams. */
|
|
7
7
|
export * as Sse from './Sse.js';
|
|
8
|
+
/** Server-side automatic settlement schedule, and its raw-unit form `superviseCloses` takes. */
|
|
9
|
+
export { resolveSettlementSchedule } from './Settlement.js';
|
|
8
10
|
//# sourceMappingURL=index.js.map
|