From 1b0099ab2f29afd0926bc7727122c7108060a615 Mon Sep 17 00:00:00 2001 From: Haakam Aujla Date: Thu, 26 Feb 2026 02:31:36 -0800 Subject: [PATCH 1/5] Rename mpp to mppx --- src/wrapper/Client.ts | 16 ++++++++-------- src/wrapper/{mpp-types.d.ts => mppx-types.d.ts} | 0 src/wrapper/{mpp.ts => mppx.ts} | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/wrapper/{mpp-types.d.ts => mppx-types.d.ts} (100%) rename src/wrapper/{mpp.ts => mppx.ts} (52%) diff --git a/src/wrapper/Client.ts b/src/wrapper/Client.ts index 741cf2e..5b0bf72 100644 --- a/src/wrapper/Client.ts +++ b/src/wrapper/Client.ts @@ -8,16 +8,16 @@ import { AgentMailClient as FernAgentMailClient } from "../Client.js"; import { type GetPaymentCredentials, WebsocketsClient } from "./WebsocketsClient.js"; import { getPaymentCredentials as getX402Credentials } from "./x402.js"; -import { getPaymentCredentials as getMppCredentials } from "./mpp.js"; +import { getPaymentCredentials as getMppCredentials } from "./mppx.js"; type SharedOptions = Omit; export declare namespace AgentMailClient { export type Options = SharedOptions & ( - | { x402: x402Client; mpp?: never; apiKey?: never } - | { mpp: Mppx.Mppx; x402?: never; apiKey?: never } - | { apiKey?: Supplier; x402?: never; mpp?: never } + | { x402: x402Client; mppx?: never; apiKey?: never } + | { mppx: Mppx.Mppx; x402?: never; apiKey?: never } + | { apiKey?: Supplier; x402?: never; mppx?: never } ); export type RequestOptions = FernAgentMailClient.RequestOptions; } @@ -54,13 +54,13 @@ export class AgentMailClient extends FernAgentMailClient { super(fernOptions); this._getPaymentCredentials = (wsUrl) => getX402Credentials(wsUrl, x402); - } else if (options.mpp) { - const { mpp, ...rest } = options; + } else if (options.mppx) { + const { mppx, ...rest } = options; const fernOptions = { ...rest, authProvider: new NoOpAuthProvider(), - fetch: mpp.fetch, + fetch: mppx.fetch, }; if (!fernOptions.environment && !fernOptions.baseUrl) { @@ -69,7 +69,7 @@ export class AgentMailClient extends FernAgentMailClient { super(fernOptions); - this._getPaymentCredentials = (wsUrl) => getMppCredentials(wsUrl, mpp); + this._getPaymentCredentials = (wsUrl) => getMppCredentials(wsUrl, mppx); } else { let fernOptions: FernAgentMailClient.Options = options; diff --git a/src/wrapper/mpp-types.d.ts b/src/wrapper/mppx-types.d.ts similarity index 100% rename from src/wrapper/mpp-types.d.ts rename to src/wrapper/mppx-types.d.ts diff --git a/src/wrapper/mpp.ts b/src/wrapper/mppx.ts similarity index 52% rename from src/wrapper/mpp.ts rename to src/wrapper/mppx.ts index 08d95ec..0bf8520 100644 --- a/src/wrapper/mpp.ts +++ b/src/wrapper/mppx.ts @@ -1,11 +1,11 @@ import type { Mppx } from "mppx/client"; import { probe402 } from "./probe402.js"; -export async function getPaymentCredentials(wsUrl: string, mpp: Mppx.Mppx): Promise> { +export async function getPaymentCredentials(wsUrl: string, mppx: Mppx.Mppx): Promise> { const response = await probe402(wsUrl); - const credential = await mpp.createCredential(response); - const signed = mpp.transport.setCredential(new Request(wsUrl), credential); + const credential = await mppx.createCredential(response); + const signed = mppx.transport.setCredential(new Request(wsUrl), credential); const headers: Record = {}; signed.headers.forEach((value, key) => { From 093160b8e3b4a301ea20af673527adf9181aaab2 Mon Sep 17 00:00:00 2001 From: Haakam Aujla Date: Thu, 26 Feb 2026 02:37:22 -0800 Subject: [PATCH 2/5] Fix --- tests/unit/wrapper/Client.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit/wrapper/Client.test.ts b/tests/unit/wrapper/Client.test.ts index 335ae37..d0baa91 100644 --- a/tests/unit/wrapper/Client.test.ts +++ b/tests/unit/wrapper/Client.test.ts @@ -102,7 +102,7 @@ describe("AgentMailClient environment selection", () => { }); }); - describe("with mpp", () => { + describe("with mppx", () => { const mockMppFetch = vi.fn().mockResolvedValue(new Response()); const mockMppClient = { fetch: mockMppFetch, @@ -111,22 +111,22 @@ describe("AgentMailClient environment selection", () => { }; it("should derive ProdMpp environment", async () => { - const client = new AgentMailClient({ mpp: mockMppClient }); + const client = new AgentMailClient({ mppx: mockMppClient }); const env = await Supplier.get(client["_options"].environment); expect(env).toEqual(AgentMailEnvironment.ProdMpp); }); it("should not override explicitly set environment", async () => { const client = new AgentMailClient({ - mpp: mockMppClient, + mppx: mockMppClient, environment: AgentMailEnvironment.EuProd, }); const env = await Supplier.get(client["_options"].environment); expect(env).toEqual(AgentMailEnvironment.EuProd); }); - it("should use mpp.fetch as the fetch implementation", () => { - const client = new AgentMailClient({ mpp: mockMppClient }); + it("should use mppx.fetch as the fetch implementation", () => { + const client = new AgentMailClient({ mppx: mockMppClient }); expect(client["_options"].fetch).toBe(mockMppFetch); }); }); From ca6edf19a29860224664dafe9ed8ae3b3e26b4a4 Mon Sep 17 00:00:00 2001 From: Haakam Aujla Date: Fri, 27 Feb 2026 02:06:00 -0800 Subject: [PATCH 3/5] Refine MPP --- src/wrapper/Client.ts | 5 ++--- src/wrapper/mppx-types.d.ts | 11 ----------- src/wrapper/mppx.ts | 13 ++++++++++--- tests/unit/wrapper/WebsocketsClient.test.ts | 8 ++++---- 4 files changed, 16 insertions(+), 21 deletions(-) delete mode 100644 src/wrapper/mppx-types.d.ts diff --git a/src/wrapper/Client.ts b/src/wrapper/Client.ts index 5b0bf72..898eb30 100644 --- a/src/wrapper/Client.ts +++ b/src/wrapper/Client.ts @@ -1,5 +1,4 @@ import type { x402Client } from "@x402/fetch"; -import type { Mppx } from "mppx/client"; import { Supplier } from "../core/index.js"; import { NoOpAuthProvider } from "../core/auth/NoOpAuthProvider.js"; @@ -8,7 +7,7 @@ import { AgentMailClient as FernAgentMailClient } from "../Client.js"; import { type GetPaymentCredentials, WebsocketsClient } from "./WebsocketsClient.js"; import { getPaymentCredentials as getX402Credentials } from "./x402.js"; -import { getPaymentCredentials as getMppCredentials } from "./mppx.js"; +import { type MppxClient, getPaymentCredentials as getMppCredentials } from "./mppx.js"; type SharedOptions = Omit; @@ -16,7 +15,7 @@ export declare namespace AgentMailClient { export type Options = SharedOptions & ( | { x402: x402Client; mppx?: never; apiKey?: never } - | { mppx: Mppx.Mppx; x402?: never; apiKey?: never } + | { mppx: MppxClient; x402?: never; apiKey?: never } | { apiKey?: Supplier; x402?: never; mppx?: never } ); export type RequestOptions = FernAgentMailClient.RequestOptions; diff --git a/src/wrapper/mppx-types.d.ts b/src/wrapper/mppx-types.d.ts deleted file mode 100644 index 6efe36c..0000000 --- a/src/wrapper/mppx-types.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -declare module "mppx/client" { - export namespace Mppx { - type Mppx = { - fetch: typeof globalThis.fetch; - transport: { - setCredential(request: Request, credential: string): Request; - }; - createCredential(response: Response): Promise; - }; - } -} diff --git a/src/wrapper/mppx.ts b/src/wrapper/mppx.ts index 0bf8520..8ac3cae 100644 --- a/src/wrapper/mppx.ts +++ b/src/wrapper/mppx.ts @@ -1,14 +1,21 @@ -import type { Mppx } from "mppx/client"; import { probe402 } from "./probe402.js"; -export async function getPaymentCredentials(wsUrl: string, mppx: Mppx.Mppx): Promise> { +export interface MppxClient { + fetch: typeof globalThis.fetch; + transport: { + setCredential(request: Request, credential: string): Request; + }; + createCredential(response: Response): Promise; +} + +export async function getPaymentCredentials(wsUrl: string, mppx: MppxClient): Promise> { const response = await probe402(wsUrl); const credential = await mppx.createCredential(response); const signed = mppx.transport.setCredential(new Request(wsUrl), credential); const headers: Record = {}; - signed.headers.forEach((value, key) => { + signed.headers.forEach((value: string, key: string) => { headers[key] = value; }); return headers; diff --git a/tests/unit/wrapper/WebsocketsClient.test.ts b/tests/unit/wrapper/WebsocketsClient.test.ts index 741ce46..b78d204 100644 --- a/tests/unit/wrapper/WebsocketsClient.test.ts +++ b/tests/unit/wrapper/WebsocketsClient.test.ts @@ -2,7 +2,7 @@ import { AgentMailClient } from "../../../src/wrapper/Client"; import { WebsocketsClient as FernWebsocketsClient } from "../../../src/api/resources/websockets/client/Client"; import type { WebsocketsSocket } from "../../../src/api/resources/websockets/client/Socket"; import * as x402Helpers from "../../../src/wrapper/x402"; -import * as mppHelpers from "../../../src/wrapper/mpp"; +import * as mppHelpers from "../../../src/wrapper/mppx"; function mockConnect() { return vi.spyOn(FernWebsocketsClient.prototype, "connect").mockResolvedValue({} as WebsocketsSocket); @@ -94,7 +94,7 @@ describe("WebsocketsClient wrapper", () => { }); }); - describe("with mpp", () => { + describe("with mppx", () => { const mockMppClient = { fetch: vi.fn(), transport: { setCredential: vi.fn() }, @@ -105,7 +105,7 @@ describe("WebsocketsClient wrapper", () => { it("should call getPaymentCredentials and pass as queryParams", async () => { const spy = vi.spyOn(mppHelpers, "getPaymentCredentials").mockResolvedValue(mockCredentials); - const client = new AgentMailClient({ mpp: mockMppClient }); + const client = new AgentMailClient({ mppx: mockMppClient }); await client.websockets.connect(); expect(spy).toHaveBeenCalled(); @@ -121,7 +121,7 @@ describe("WebsocketsClient wrapper", () => { it("should let user queryParams override payment credentials", async () => { const spy = vi.spyOn(mppHelpers, "getPaymentCredentials").mockResolvedValue({ Authorization: "from-mpp" }); - const client = new AgentMailClient({ mpp: mockMppClient }); + const client = new AgentMailClient({ mppx: mockMppClient }); await client.websockets.connect({ queryParams: { Authorization: "user-override" } }); expect(connectSpy).toHaveBeenCalledWith( From 884f8ccf43eee1b07630cab866a6067e9c529514 Mon Sep 17 00:00:00 2001 From: Haakam Aujla Date: Fri, 27 Feb 2026 02:20:14 -0800 Subject: [PATCH 4/5] Fix --- src/wrapper/mppx.ts | 36 +++++++++++++++++++++--------------- src/wrapper/probe402.ts | 9 --------- src/wrapper/util.ts | 14 ++++++++++++++ src/wrapper/x402.ts | 36 +++++++++++++++++++++--------------- 4 files changed, 56 insertions(+), 39 deletions(-) delete mode 100644 src/wrapper/probe402.ts create mode 100644 src/wrapper/util.ts diff --git a/src/wrapper/mppx.ts b/src/wrapper/mppx.ts index 8ac3cae..5c2d112 100644 --- a/src/wrapper/mppx.ts +++ b/src/wrapper/mppx.ts @@ -1,22 +1,28 @@ -import { probe402 } from "./probe402.js"; +import { probe402, wsToHttp } from "./util.js"; export interface MppxClient { - fetch: typeof globalThis.fetch; - transport: { - setCredential(request: Request, credential: string): Request; - }; - createCredential(response: Response): Promise; + fetch: typeof globalThis.fetch; + transport: { + setCredential(request: Request, credential: string): Request; + }; + createCredential(response: Response): Promise; } -export async function getPaymentCredentials(wsUrl: string, mppx: MppxClient): Promise> { - const response = await probe402(wsUrl); +export async function getPaymentCredentials( + wsUrl: string, + mppx: MppxClient +): Promise> { + const response = await probe402(wsUrl); - const credential = await mppx.createCredential(response); - const signed = mppx.transport.setCredential(new Request(wsUrl), credential); + const credential = await mppx.createCredential(response); + const signed = mppx.transport.setCredential( + new Request(wsToHttp(wsUrl)), + credential + ); - const headers: Record = {}; - signed.headers.forEach((value: string, key: string) => { - headers[key] = value; - }); - return headers; + const headers: Record = {}; + signed.headers.forEach((value: string, key: string) => { + headers[key] = value; + }); + return headers; } diff --git a/src/wrapper/probe402.ts b/src/wrapper/probe402.ts deleted file mode 100644 index 1ff87be..0000000 --- a/src/wrapper/probe402.ts +++ /dev/null @@ -1,9 +0,0 @@ -export async function probe402(wsUrl: string): Promise { - const httpUrl = wsUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://"); - - const response = await fetch(httpUrl); - if (response.status !== 402) { - throw new Error(`Expected 402 from ${httpUrl} but got ${response.status}`); - } - return response; -} diff --git a/src/wrapper/util.ts b/src/wrapper/util.ts new file mode 100644 index 0000000..59f7dc9 --- /dev/null +++ b/src/wrapper/util.ts @@ -0,0 +1,14 @@ +export function wsToHttp(wsUrl: string): string { + return wsUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://"); +} + +export async function probe402(wsUrl: string): Promise { + const httpUrl = wsToHttp(wsUrl); + + const response = await fetch(httpUrl); + if (response.status !== 402) { + throw new Error(`Expected 402 from ${httpUrl} but got ${response.status}`); + } + + return response; +} diff --git a/src/wrapper/x402.ts b/src/wrapper/x402.ts index cb1e7bc..d16a23a 100644 --- a/src/wrapper/x402.ts +++ b/src/wrapper/x402.ts @@ -1,22 +1,28 @@ import type { x402Client } from "@x402/fetch"; -import { probe402 } from "./probe402.js"; +import { probe402 } from "./util.js"; -export async function getPaymentCredentials(wsUrl: string, client: x402Client): Promise> { - const { x402HTTPClient } = await import("@x402/fetch"); - const httpClient = new x402HTTPClient(client); +export async function getPaymentCredentials( + wsUrl: string, + client: x402Client +): Promise> { + const { x402HTTPClient } = await import("@x402/fetch"); + const httpClient = new x402HTTPClient(client); - const response = await probe402(wsUrl); + const response = await probe402(wsUrl); - let body: unknown; - try { - body = JSON.parse(await response.text()); - } catch { - body = undefined; - } + let body: unknown; + try { + body = JSON.parse(await response.text()); + } catch { + body = undefined; + } - const getHeader = (name: string) => response.headers.get(name); - const paymentRequired = httpClient.getPaymentRequiredResponse(getHeader, body); + const getHeader = (name: string) => response.headers.get(name); + const paymentRequired = httpClient.getPaymentRequiredResponse( + getHeader, + body + ); - const paymentPayload = await httpClient.createPaymentPayload(paymentRequired); - return httpClient.encodePaymentSignatureHeader(paymentPayload); + const paymentPayload = await httpClient.createPaymentPayload(paymentRequired); + return httpClient.encodePaymentSignatureHeader(paymentPayload); } From e929ebfad4a1f4b3e183f1c5bf93accb2f31d538 Mon Sep 17 00:00:00 2001 From: Haakam Aujla Date: Fri, 27 Feb 2026 02:25:32 -0800 Subject: [PATCH 5/5] Format --- src/wrapper/Client.ts | 4 ++-- src/wrapper/mppx.ts | 34 ++++++++++++++-------------------- src/wrapper/util.ts | 14 +++++++------- src/wrapper/x402.ts | 34 ++++++++++++++-------------------- 4 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/wrapper/Client.ts b/src/wrapper/Client.ts index 898eb30..c00aec3 100644 --- a/src/wrapper/Client.ts +++ b/src/wrapper/Client.ts @@ -39,8 +39,8 @@ export class AgentMailClient extends FernAgentMailClient { authProvider: new NoOpAuthProvider(), fetch: async (input: RequestInfo | URL, init?: RequestInit) => { if (!wrappedFetch) { - const mod = await import("@x402/fetch"); - wrappedFetch = mod.wrapFetchWithPayment(fetch, x402); + const { wrapFetchWithPayment } = await import("@x402/fetch"); + wrappedFetch = wrapFetchWithPayment(fetch, x402); } return wrappedFetch(input, init); }, diff --git a/src/wrapper/mppx.ts b/src/wrapper/mppx.ts index 5c2d112..d61053e 100644 --- a/src/wrapper/mppx.ts +++ b/src/wrapper/mppx.ts @@ -1,28 +1,22 @@ import { probe402, wsToHttp } from "./util.js"; export interface MppxClient { - fetch: typeof globalThis.fetch; - transport: { - setCredential(request: Request, credential: string): Request; - }; - createCredential(response: Response): Promise; + fetch: typeof globalThis.fetch; + transport: { + setCredential(request: Request, credential: string): Request; + }; + createCredential(response: Response): Promise; } -export async function getPaymentCredentials( - wsUrl: string, - mppx: MppxClient -): Promise> { - const response = await probe402(wsUrl); +export async function getPaymentCredentials(wsUrl: string, mppx: MppxClient): Promise> { + const response = await probe402(wsUrl); - const credential = await mppx.createCredential(response); - const signed = mppx.transport.setCredential( - new Request(wsToHttp(wsUrl)), - credential - ); + const credential = await mppx.createCredential(response); + const signed = mppx.transport.setCredential(new Request(wsToHttp(wsUrl)), credential); - const headers: Record = {}; - signed.headers.forEach((value: string, key: string) => { - headers[key] = value; - }); - return headers; + const headers: Record = {}; + signed.headers.forEach((value: string, key: string) => { + headers[key] = value; + }); + return headers; } diff --git a/src/wrapper/util.ts b/src/wrapper/util.ts index 59f7dc9..39609ee 100644 --- a/src/wrapper/util.ts +++ b/src/wrapper/util.ts @@ -1,14 +1,14 @@ export function wsToHttp(wsUrl: string): string { - return wsUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://"); + return wsUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://"); } export async function probe402(wsUrl: string): Promise { - const httpUrl = wsToHttp(wsUrl); + const httpUrl = wsToHttp(wsUrl); - const response = await fetch(httpUrl); - if (response.status !== 402) { - throw new Error(`Expected 402 from ${httpUrl} but got ${response.status}`); - } + const response = await fetch(httpUrl); + if (response.status !== 402) { + throw new Error(`Expected 402 from ${httpUrl} but got ${response.status}`); + } - return response; + return response; } diff --git a/src/wrapper/x402.ts b/src/wrapper/x402.ts index d16a23a..cae25c7 100644 --- a/src/wrapper/x402.ts +++ b/src/wrapper/x402.ts @@ -1,28 +1,22 @@ import type { x402Client } from "@x402/fetch"; import { probe402 } from "./util.js"; -export async function getPaymentCredentials( - wsUrl: string, - client: x402Client -): Promise> { - const { x402HTTPClient } = await import("@x402/fetch"); - const httpClient = new x402HTTPClient(client); +export async function getPaymentCredentials(wsUrl: string, client: x402Client): Promise> { + const { x402HTTPClient } = await import("@x402/fetch"); + const httpClient = new x402HTTPClient(client); - const response = await probe402(wsUrl); + const response = await probe402(wsUrl); - let body: unknown; - try { - body = JSON.parse(await response.text()); - } catch { - body = undefined; - } + let body: unknown; + try { + body = JSON.parse(await response.text()); + } catch { + body = undefined; + } - const getHeader = (name: string) => response.headers.get(name); - const paymentRequired = httpClient.getPaymentRequiredResponse( - getHeader, - body - ); + const getHeader = (name: string) => response.headers.get(name); + const paymentRequired = httpClient.getPaymentRequiredResponse(getHeader, body); - const paymentPayload = await httpClient.createPaymentPayload(paymentRequired); - return httpClient.encodePaymentSignatureHeader(paymentPayload); + const paymentPayload = await httpClient.createPaymentPayload(paymentRequired); + return httpClient.encodePaymentSignatureHeader(paymentPayload); }