mppx 0.0.1 → 0.1.0
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/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/BodyDigest.d.ts +42 -0
- package/dist/BodyDigest.d.ts.map +1 -0
- package/dist/BodyDigest.js +40 -0
- package/dist/BodyDigest.js.map +1 -0
- package/dist/Challenge.d.ts +271 -0
- package/dist/Challenge.d.ts.map +1 -0
- package/dist/Challenge.js +291 -0
- package/dist/Challenge.js.map +1 -0
- package/dist/Credential.d.ts +91 -0
- package/dist/Credential.d.ts.map +1 -0
- package/dist/Credential.js +122 -0
- package/dist/Credential.js.map +1 -0
- package/dist/Errors.d.ts +243 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Errors.js +201 -0
- package/dist/Errors.js.map +1 -0
- package/dist/Expires.d.ts +15 -0
- package/dist/Expires.d.ts.map +1 -0
- package/dist/Expires.js +29 -0
- package/dist/Expires.js.map +1 -0
- package/dist/Intent.d.ts +101 -0
- package/dist/Intent.d.ts.map +1 -0
- package/dist/Intent.js +83 -0
- package/dist/Intent.js.map +1 -0
- package/dist/Mcp.d.ts +74 -0
- package/dist/Mcp.d.ts.map +1 -0
- package/dist/Mcp.js +9 -0
- package/dist/Mcp.js.map +1 -0
- package/dist/MethodIntent.d.ts +225 -0
- package/dist/MethodIntent.d.ts.map +1 -0
- package/dist/MethodIntent.js +156 -0
- package/dist/MethodIntent.js.map +1 -0
- package/dist/PaymentRequest.d.ts +88 -0
- package/dist/PaymentRequest.d.ts.map +1 -0
- package/dist/PaymentRequest.js +81 -0
- package/dist/PaymentRequest.js.map +1 -0
- package/dist/Receipt.d.ts +110 -0
- package/dist/Receipt.d.ts.map +1 -0
- package/dist/Receipt.js +105 -0
- package/dist/Receipt.js.map +1 -0
- package/dist/Store.d.ts +28 -0
- package/dist/Store.d.ts.map +1 -0
- package/dist/Store.js +61 -0
- package/dist/Store.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1219 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/Methods.d.ts +4 -0
- package/dist/client/Methods.d.ts.map +1 -0
- package/dist/client/Methods.js +4 -0
- package/dist/client/Methods.js.map +1 -0
- package/dist/client/Mppx.d.ts +84 -0
- package/dist/client/Mppx.d.ts.map +1 -0
- package/dist/client/Mppx.js +64 -0
- package/dist/client/Mppx.js.map +1 -0
- package/dist/client/Transport.d.ts +56 -0
- package/dist/client/Transport.d.ts.map +1 -0
- package/dist/client/Transport.js +81 -0
- package/dist/client/Transport.js.map +1 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +5 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/internal/Fetch.d.ts +85 -0
- package/dist/client/internal/Fetch.d.ts.map +1 -0
- package/dist/client/internal/Fetch.js +95 -0
- package/dist/client/internal/Fetch.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/types.d.ts +302 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +2 -0
- package/dist/internal/types.js.map +1 -0
- package/dist/mcp-sdk/client/McpClient.d.ts +78 -0
- package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -0
- package/dist/mcp-sdk/client/McpClient.js +98 -0
- package/dist/mcp-sdk/client/McpClient.js.map +1 -0
- package/dist/mcp-sdk/client/index.d.ts +3 -0
- package/dist/mcp-sdk/client/index.d.ts.map +1 -0
- package/dist/mcp-sdk/client/index.js +3 -0
- package/dist/mcp-sdk/client/index.js.map +1 -0
- package/dist/mcp-sdk/server/Transport.d.ts +43 -0
- package/dist/mcp-sdk/server/Transport.d.ts.map +1 -0
- package/dist/mcp-sdk/server/Transport.js +71 -0
- package/dist/mcp-sdk/server/Transport.js.map +1 -0
- package/dist/mcp-sdk/server/index.d.ts +2 -0
- package/dist/mcp-sdk/server/index.d.ts.map +1 -0
- package/dist/mcp-sdk/server/index.js +2 -0
- package/dist/mcp-sdk/server/index.js.map +1 -0
- package/dist/middlewares/elysia.d.ts +51 -0
- package/dist/middlewares/elysia.d.ts.map +1 -0
- package/dist/middlewares/elysia.js +59 -0
- package/dist/middlewares/elysia.js.map +1 -0
- package/dist/middlewares/express.d.ts +46 -0
- package/dist/middlewares/express.d.ts.map +1 -0
- package/dist/middlewares/express.js +69 -0
- package/dist/middlewares/express.js.map +1 -0
- package/dist/middlewares/hono.d.ts +46 -0
- package/dist/middlewares/hono.d.ts.map +1 -0
- package/dist/middlewares/hono.js +57 -0
- package/dist/middlewares/hono.js.map +1 -0
- package/dist/middlewares/internal/mppx.d.ts +16 -0
- package/dist/middlewares/internal/mppx.d.ts.map +1 -0
- package/dist/middlewares/internal/mppx.js +16 -0
- package/dist/middlewares/internal/mppx.js.map +1 -0
- package/dist/middlewares/nextjs.d.ts +45 -0
- package/dist/middlewares/nextjs.d.ts.map +1 -0
- package/dist/middlewares/nextjs.js +57 -0
- package/dist/middlewares/nextjs.js.map +1 -0
- package/dist/proxy/Proxy.d.ts +47 -0
- package/dist/proxy/Proxy.d.ts.map +1 -0
- package/dist/proxy/Proxy.js +126 -0
- package/dist/proxy/Proxy.js.map +1 -0
- package/dist/proxy/Service.d.ts +100 -0
- package/dist/proxy/Service.d.ts.map +1 -0
- package/dist/proxy/Service.js +147 -0
- package/dist/proxy/Service.js.map +1 -0
- package/dist/proxy/index.d.ts +7 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +7 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/internal/Headers.d.ts +3 -0
- package/dist/proxy/internal/Headers.d.ts.map +1 -0
- package/dist/proxy/internal/Headers.js +41 -0
- package/dist/proxy/internal/Headers.js.map +1 -0
- package/dist/proxy/internal/Route.d.ts +14 -0
- package/dist/proxy/internal/Route.d.ts.map +1 -0
- package/dist/proxy/internal/Route.js +47 -0
- package/dist/proxy/internal/Route.js.map +1 -0
- package/dist/proxy/services/anthropic.d.ts +29 -0
- package/dist/proxy/services/anthropic.d.ts.map +1 -0
- package/dist/proxy/services/anthropic.js +30 -0
- package/dist/proxy/services/anthropic.js.map +1 -0
- package/dist/proxy/services/openai.d.ts +29 -0
- package/dist/proxy/services/openai.d.ts.map +1 -0
- package/dist/proxy/services/openai.js +30 -0
- package/dist/proxy/services/openai.js.map +1 -0
- package/dist/proxy/services/stripe.d.ts +29 -0
- package/dist/proxy/services/stripe.d.ts.map +1 -0
- package/dist/proxy/services/stripe.js +30 -0
- package/dist/proxy/services/stripe.js.map +1 -0
- package/dist/server/Methods.d.ts +3 -0
- package/dist/server/Methods.d.ts.map +1 -0
- package/dist/server/Methods.js +3 -0
- package/dist/server/Methods.js.map +1 -0
- package/dist/server/Mppx.d.ts +116 -0
- package/dist/server/Mppx.d.ts.map +1 -0
- package/dist/server/Mppx.js +207 -0
- package/dist/server/Mppx.js.map +1 -0
- package/dist/server/NodeListener.d.ts +3 -0
- package/dist/server/NodeListener.d.ts.map +1 -0
- package/dist/server/NodeListener.js +3 -0
- package/dist/server/NodeListener.js.map +1 -0
- package/dist/server/Request.d.ts +24 -0
- package/dist/server/Request.d.ts.map +1 -0
- package/dist/server/Request.js +26 -0
- package/dist/server/Request.js.map +1 -0
- package/dist/server/Response.d.ts +10 -0
- package/dist/server/Response.d.ts.map +1 -0
- package/dist/server/Response.js +15 -0
- package/dist/server/Response.js.map +1 -0
- package/dist/server/Transport.d.ts +93 -0
- package/dist/server/Transport.d.ts.map +1 -0
- package/dist/server/Transport.js +132 -0
- package/dist/server/Transport.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/stripe/Intents.d.ts +54 -0
- package/dist/stripe/Intents.d.ts.map +1 -0
- package/dist/stripe/Intents.js +27 -0
- package/dist/stripe/Intents.js.map +1 -0
- package/dist/stripe/client/Charge.d.ts +114 -0
- package/dist/stripe/client/Charge.d.ts.map +1 -0
- package/dist/stripe/client/Charge.js +77 -0
- package/dist/stripe/client/Charge.js.map +1 -0
- package/dist/stripe/client/MethodIntents.d.ts +80 -0
- package/dist/stripe/client/MethodIntents.d.ts.map +1 -0
- package/dist/stripe/client/MethodIntents.js +34 -0
- package/dist/stripe/client/MethodIntents.js.map +1 -0
- package/dist/stripe/client/index.d.ts +3 -0
- package/dist/stripe/client/index.d.ts.map +1 -0
- package/dist/stripe/client/index.js +3 -0
- package/dist/stripe/client/index.js.map +1 -0
- package/dist/stripe/index.d.ts +2 -0
- package/dist/stripe/index.d.ts.map +1 -0
- package/dist/stripe/index.js +2 -0
- package/dist/stripe/index.js.map +1 -0
- package/dist/stripe/server/Charge.d.ts +74 -0
- package/dist/stripe/server/Charge.d.ts.map +1 -0
- package/dist/stripe/server/Charge.js +79 -0
- package/dist/stripe/server/Charge.js.map +1 -0
- package/dist/stripe/server/MethodIntents.d.ts +65 -0
- package/dist/stripe/server/MethodIntents.d.ts.map +1 -0
- package/dist/stripe/server/MethodIntents.js +21 -0
- package/dist/stripe/server/MethodIntents.js.map +1 -0
- package/dist/stripe/server/index.d.ts +3 -0
- package/dist/stripe/server/index.d.ts.map +1 -0
- package/dist/stripe/server/index.js +3 -0
- package/dist/stripe/server/index.js.map +1 -0
- package/dist/tempo/Attribution.d.ts +101 -0
- package/dist/tempo/Attribution.d.ts.map +1 -0
- package/dist/tempo/Attribution.js +124 -0
- package/dist/tempo/Attribution.js.map +1 -0
- package/dist/tempo/Intents.d.ts +132 -0
- package/dist/tempo/Intents.d.ts.map +1 -0
- package/dist/tempo/Intents.js +81 -0
- package/dist/tempo/Intents.js.map +1 -0
- package/dist/tempo/client/ChannelOps.d.ts +54 -0
- package/dist/tempo/client/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/client/ChannelOps.js +138 -0
- package/dist/tempo/client/ChannelOps.js.map +1 -0
- package/dist/tempo/client/Charge.d.ts +76 -0
- package/dist/tempo/client/Charge.d.ts.map +1 -0
- package/dist/tempo/client/Charge.js +69 -0
- package/dist/tempo/client/Charge.js.map +1 -0
- package/dist/tempo/client/MethodIntents.d.ts +157 -0
- package/dist/tempo/client/MethodIntents.d.ts.map +1 -0
- package/dist/tempo/client/MethodIntents.js +25 -0
- package/dist/tempo/client/MethodIntents.js.map +1 -0
- package/dist/tempo/client/Session.d.ts +159 -0
- package/dist/tempo/client/Session.d.ts.map +1 -0
- package/dist/tempo/client/Session.js +263 -0
- package/dist/tempo/client/Session.js.map +1 -0
- package/dist/tempo/client/SessionManager.d.ts +62 -0
- package/dist/tempo/client/SessionManager.d.ts.map +1 -0
- package/dist/tempo/client/SessionManager.js +196 -0
- package/dist/tempo/client/SessionManager.js.map +1 -0
- package/dist/tempo/client/index.d.ts +6 -0
- package/dist/tempo/client/index.d.ts.map +1 -0
- package/dist/tempo/client/index.js +5 -0
- package/dist/tempo/client/index.js.map +1 -0
- package/dist/tempo/index.d.ts +3 -0
- package/dist/tempo/index.d.ts.map +1 -0
- package/dist/tempo/index.js +3 -0
- package/dist/tempo/index.js.map +1 -0
- package/dist/tempo/internal/account.d.ts +32 -0
- package/dist/tempo/internal/account.d.ts.map +1 -0
- package/dist/tempo/internal/account.js +33 -0
- package/dist/tempo/internal/account.js.map +1 -0
- package/dist/tempo/internal/defaults.d.ts +18 -0
- package/dist/tempo/internal/defaults.d.ts.map +1 -0
- package/dist/tempo/internal/defaults.js +18 -0
- package/dist/tempo/internal/defaults.js.map +1 -0
- package/dist/tempo/internal/types.d.ts +11 -0
- package/dist/tempo/internal/types.d.ts.map +1 -0
- package/dist/tempo/internal/types.js +2 -0
- package/dist/tempo/internal/types.js.map +1 -0
- package/dist/tempo/server/Charge.d.ts +77 -0
- package/dist/tempo/server/Charge.d.ts.map +1 -0
- package/dist/tempo/server/Charge.js +228 -0
- package/dist/tempo/server/Charge.js.map +1 -0
- package/dist/tempo/server/MethodIntents.d.ts +140 -0
- package/dist/tempo/server/MethodIntents.d.ts.map +1 -0
- package/dist/tempo/server/MethodIntents.js +26 -0
- package/dist/tempo/server/MethodIntents.js.map +1 -0
- package/dist/tempo/server/Session.d.ts +148 -0
- package/dist/tempo/server/Session.d.ts.map +1 -0
- package/dist/tempo/server/Session.js +529 -0
- package/dist/tempo/server/Session.js.map +1 -0
- package/dist/tempo/server/internal/transport.d.ts +47 -0
- package/dist/tempo/server/internal/transport.d.ts.map +1 -0
- package/dist/tempo/server/internal/transport.js +118 -0
- package/dist/tempo/server/internal/transport.js.map +1 -0
- package/dist/tempo/stream/Chain.d.ts +52 -0
- package/dist/tempo/stream/Chain.d.ts.map +1 -0
- package/dist/tempo/stream/Chain.js +215 -0
- package/dist/tempo/stream/Chain.js.map +1 -0
- package/dist/tempo/stream/Channel.d.ts +26 -0
- package/dist/tempo/stream/Channel.d.ts.map +1 -0
- package/dist/tempo/stream/Channel.js +27 -0
- package/dist/tempo/stream/Channel.js.map +1 -0
- package/dist/tempo/stream/ChannelStore.d.ts +103 -0
- package/dist/tempo/stream/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/stream/ChannelStore.js +100 -0
- package/dist/tempo/stream/ChannelStore.js.map +1 -0
- package/dist/tempo/stream/Receipt.d.ts +22 -0
- package/dist/tempo/stream/Receipt.d.ts.map +1 -0
- package/dist/tempo/stream/Receipt.js +34 -0
- package/dist/tempo/stream/Receipt.js.map +1 -0
- package/dist/tempo/stream/Sse.d.ts +134 -0
- package/dist/tempo/stream/Sse.d.ts.map +1 -0
- package/dist/tempo/stream/Sse.js +288 -0
- package/dist/tempo/stream/Sse.js.map +1 -0
- package/dist/tempo/stream/Types.d.ts +78 -0
- package/dist/tempo/stream/Types.d.ts.map +1 -0
- package/dist/tempo/stream/Types.js +2 -0
- package/dist/tempo/stream/Types.js.map +1 -0
- package/dist/tempo/stream/Voucher.d.ts +20 -0
- package/dist/tempo/stream/Voucher.d.ts.map +1 -0
- package/dist/tempo/stream/Voucher.js +98 -0
- package/dist/tempo/stream/Voucher.js.map +1 -0
- package/dist/tempo/stream/escrow.abi.d.ts +598 -0
- package/dist/tempo/stream/escrow.abi.d.ts.map +1 -0
- package/dist/tempo/stream/escrow.abi.js +760 -0
- package/dist/tempo/stream/escrow.abi.js.map +1 -0
- package/dist/tempo/stream/index.d.ts +8 -0
- package/dist/tempo/stream/index.d.ts.map +1 -0
- package/dist/tempo/stream/index.js +8 -0
- package/dist/tempo/stream/index.js.map +1 -0
- package/dist/viem/Account.d.ts +12 -0
- package/dist/viem/Account.d.ts.map +1 -0
- package/dist/viem/Account.js +14 -0
- package/dist/viem/Account.js.map +1 -0
- package/dist/viem/Client.d.ts +21 -0
- package/dist/viem/Client.d.ts.map +1 -0
- package/dist/viem/Client.js +19 -0
- package/dist/viem/Client.js.map +1 -0
- package/dist/zod.d.ts +17 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +35 -0
- package/dist/zod.js.map +1 -0
- package/package.json +117 -4
- package/src/BodyDigest.test.ts +43 -0
- package/src/BodyDigest.ts +53 -0
- package/src/Challenge.test-d.ts +81 -0
- package/src/Challenge.test.ts +414 -0
- package/src/Challenge.ts +429 -0
- package/src/Credential.test.ts +227 -0
- package/src/Credential.ts +154 -0
- package/src/Errors.test.ts +402 -0
- package/src/Errors.ts +348 -0
- package/src/Expires.ts +34 -0
- package/src/Intent.test.ts +180 -0
- package/src/Intent.ts +109 -0
- package/src/Mcp.ts +81 -0
- package/src/MethodIntent.test.ts +303 -0
- package/src/MethodIntent.ts +388 -0
- package/src/PaymentRequest.test.ts +152 -0
- package/src/PaymentRequest.ts +107 -0
- package/src/Receipt.test.ts +98 -0
- package/src/Receipt.ts +129 -0
- package/src/Store.ts +84 -0
- package/src/cli.test.ts +542 -0
- package/src/cli.ts +1319 -0
- package/src/client/Methods.ts +3 -0
- package/src/client/Mppx.test-d.ts +90 -0
- package/src/client/Mppx.test.ts +468 -0
- package/src/client/Mppx.ts +149 -0
- package/src/client/Transport.test.ts +283 -0
- package/src/client/Transport.ts +115 -0
- package/src/client/index.ts +4 -0
- package/src/client/internal/Fetch.test-d.ts +57 -0
- package/src/client/internal/Fetch.test.ts +281 -0
- package/src/client/internal/Fetch.ts +157 -0
- package/src/env.d.ts +11 -0
- package/src/index.ts +12 -0
- package/src/internal/types.ts +403 -0
- package/src/mcp-sdk/client/McpClient.test-d.ts +109 -0
- package/src/mcp-sdk/client/McpClient.test.ts +219 -0
- package/src/mcp-sdk/client/McpClient.ts +187 -0
- package/src/mcp-sdk/client/index.ts +2 -0
- package/src/mcp-sdk/server/Transport.ts +94 -0
- package/src/mcp-sdk/server/index.ts +1 -0
- package/src/middlewares/elysia.ts +66 -0
- package/src/middlewares/express.test.ts +155 -0
- package/src/middlewares/express.ts +82 -0
- package/src/middlewares/hono.test.ts +148 -0
- package/src/middlewares/hono.ts +62 -0
- package/src/middlewares/internal/mppx.ts +30 -0
- package/src/middlewares/nextjs.test.ts +164 -0
- package/src/middlewares/nextjs.ts +66 -0
- package/src/proxy/Proxy.test.ts +472 -0
- package/src/proxy/Proxy.ts +175 -0
- package/src/proxy/Service.test.ts +125 -0
- package/src/proxy/Service.ts +227 -0
- package/src/proxy/index.ts +6 -0
- package/src/proxy/internal/Headers.test.ts +100 -0
- package/src/proxy/internal/Headers.ts +40 -0
- package/src/proxy/internal/Route.test.ts +143 -0
- package/src/proxy/internal/Route.ts +54 -0
- package/src/proxy/services/anthropic.ts +45 -0
- package/src/proxy/services/openai.test.ts +97 -0
- package/src/proxy/services/openai.ts +48 -0
- package/src/proxy/services/stripe.ts +49 -0
- package/src/server/Methods.ts +2 -0
- package/src/server/Mppx.test-d.ts +343 -0
- package/src/server/Mppx.test.ts +342 -0
- package/src/server/Mppx.ts +378 -0
- package/src/server/NodeListener.test.ts +188 -0
- package/src/server/NodeListener.ts +3 -0
- package/src/server/Request.test.ts +102 -0
- package/src/server/Request.ts +33 -0
- package/src/server/Response.test.ts +31 -0
- package/src/server/Response.ts +27 -0
- package/src/server/Transport.test.ts +294 -0
- package/src/server/Transport.ts +222 -0
- package/src/server/index.ts +8 -0
- package/src/stripe/Charge.integration.test.ts +326 -0
- package/src/stripe/Intents.test.ts +52 -0
- package/src/stripe/Intents.ts +27 -0
- package/src/stripe/client/Charge.ts +119 -0
- package/src/stripe/client/MethodIntents.ts +37 -0
- package/src/stripe/client/index.ts +2 -0
- package/src/stripe/index.ts +1 -0
- package/src/stripe/server/Charge.ts +121 -0
- package/src/stripe/server/MethodIntents.ts +24 -0
- package/src/stripe/server/index.ts +2 -0
- package/src/tempo/Attribution.test.ts +187 -0
- package/src/tempo/Attribution.ts +156 -0
- package/src/tempo/Intents.test.ts +84 -0
- package/src/tempo/Intents.ts +93 -0
- package/src/tempo/client/ChannelOps.ts +233 -0
- package/src/tempo/client/Charge.ts +84 -0
- package/src/tempo/client/MethodIntents.ts +28 -0
- package/src/tempo/client/Session.ts +369 -0
- package/src/tempo/client/SessionManager.test.ts +223 -0
- package/src/tempo/client/SessionManager.ts +270 -0
- package/src/tempo/client/index.ts +5 -0
- package/src/tempo/index.ts +2 -0
- package/src/tempo/internal/account.ts +47 -0
- package/src/tempo/internal/defaults.ts +20 -0
- package/src/tempo/internal/types.ts +8 -0
- package/src/tempo/server/Charge.test.ts +847 -0
- package/src/tempo/server/Charge.ts +309 -0
- package/src/tempo/server/MethodIntents.ts +29 -0
- package/src/tempo/server/Session.test.ts +1349 -0
- package/src/tempo/server/Session.ts +773 -0
- package/src/tempo/server/Sse.test.ts +289 -0
- package/src/tempo/server/index.ts +5 -0
- package/src/tempo/server/internal/transport.ts +153 -0
- package/src/tempo/stream/Chain.ts +333 -0
- package/src/tempo/stream/Channel.ts +50 -0
- package/src/tempo/stream/ChannelStore.test.ts +473 -0
- package/src/tempo/stream/ChannelStore.ts +202 -0
- package/src/tempo/stream/Receipt.test.ts +84 -0
- package/src/tempo/stream/Receipt.ts +45 -0
- package/src/tempo/stream/Sse.test.ts +401 -0
- package/src/tempo/stream/Sse.ts +375 -0
- package/src/tempo/stream/Types.ts +86 -0
- package/src/tempo/stream/Voucher.test.ts +134 -0
- package/src/tempo/stream/Voucher.ts +123 -0
- package/src/tempo/stream/escrow.abi.ts +759 -0
- package/src/tempo/stream/index.ts +7 -0
- package/src/tsconfig.json +10 -0
- package/src/viem/Account.test.ts +71 -0
- package/src/viem/Account.ts +30 -0
- package/src/viem/Client.test.ts +58 -0
- package/src/viem/Client.ts +33 -0
- package/src/zod.ts +47 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type * as http from 'node:http'
|
|
2
|
+
import { createFetchProxy } from '@remix-run/fetch-proxy'
|
|
3
|
+
import * as Request from '../server/Request.js'
|
|
4
|
+
import * as Headers from './internal/Headers.js'
|
|
5
|
+
import * as Route from './internal/Route.js'
|
|
6
|
+
import * as Service from './Service.js'
|
|
7
|
+
|
|
8
|
+
/** A paid API proxy that gates upstream services behind the mppx 402 protocol. */
|
|
9
|
+
export type Proxy = {
|
|
10
|
+
/** Fetch API handler. Works with Bun, Deno, Next.js, Hono, Elysia, SvelteKit, etc. */
|
|
11
|
+
fetch: (request: Request) => Promise<Response>
|
|
12
|
+
/** Node.js request listener. Works with Express, Fastify, `http.createServer`, etc. */
|
|
13
|
+
listener: (req: http.IncomingMessage, res: http.ServerResponse) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a paid API proxy.
|
|
18
|
+
*
|
|
19
|
+
* Routes incoming requests to upstream services, injects credentials,
|
|
20
|
+
* and requires payment via the mppx 402 protocol for non-free endpoints.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { Proxy, openai } from 'mppx/proxy'
|
|
25
|
+
* import { Mppx, tempo } from 'mppx/server'
|
|
26
|
+
*
|
|
27
|
+
* const mppx = Mppx.create({ methods: [tempo()] })
|
|
28
|
+
*
|
|
29
|
+
* const proxy = Proxy.create({
|
|
30
|
+
* services: [
|
|
31
|
+
* openai({
|
|
32
|
+
* apiKey: 'sk-...',
|
|
33
|
+
* routes: {
|
|
34
|
+
* 'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
|
|
35
|
+
* 'GET /v1/models': true,
|
|
36
|
+
* },
|
|
37
|
+
* }),
|
|
38
|
+
* ],
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function create(config: create.Config): Proxy {
|
|
43
|
+
const fetchImpl = config.fetch ?? globalThis.fetch
|
|
44
|
+
|
|
45
|
+
const services = new Map(
|
|
46
|
+
config.services.map((s) => {
|
|
47
|
+
const proxy = createFetchProxy(s.baseUrl, {
|
|
48
|
+
fetch: fetchImpl,
|
|
49
|
+
rewriteCookieDomain: false,
|
|
50
|
+
rewriteCookiePath: false,
|
|
51
|
+
})
|
|
52
|
+
return [s.id, { service: s, proxy }] as const
|
|
53
|
+
}),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async function handle(request: globalThis.Request): Promise<Response> {
|
|
57
|
+
const url = new URL(request.url)
|
|
58
|
+
|
|
59
|
+
const pathname = Route.pathname(url, config.basePath)
|
|
60
|
+
|
|
61
|
+
if (!pathname) return new Response('Not Found', { status: 404 })
|
|
62
|
+
|
|
63
|
+
if (request.method === 'GET' && pathname === '/llms.txt')
|
|
64
|
+
return new Response(Service.toLlmsTxt(config.services), {
|
|
65
|
+
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
if (request.method === 'GET' && (pathname === '/services' || pathname === '/services/'))
|
|
69
|
+
return Response.json(config.services.map(Service.serialize))
|
|
70
|
+
|
|
71
|
+
{
|
|
72
|
+
const match = pathname.match(/^\/services\/([^/]+)\/?$/)
|
|
73
|
+
if (request.method === 'GET' && match) {
|
|
74
|
+
const service = config.services.find((s) => s.id === match[1])
|
|
75
|
+
if (!service) return new Response('Not Found', { status: 404 })
|
|
76
|
+
return Response.json(Service.serialize(service))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const parsed = Route.parse(pathname)
|
|
81
|
+
if (!parsed) return new Response('Not Found', { status: 404 })
|
|
82
|
+
|
|
83
|
+
const { serviceId, upstreamPath } = parsed
|
|
84
|
+
const entry = services.get(serviceId)
|
|
85
|
+
if (!entry) return new Response('Not Found', { status: 404 })
|
|
86
|
+
|
|
87
|
+
const { service, proxy } = entry
|
|
88
|
+
|
|
89
|
+
const matched =
|
|
90
|
+
Route.match(service.routes, request.method, upstreamPath) ??
|
|
91
|
+
// Management POSTs (e.g. session close) may target a path whose route
|
|
92
|
+
// is registered for a different HTTP method (e.g. GET). Fall back to
|
|
93
|
+
// path-only matching so the payment handler can process the action.
|
|
94
|
+
(request.method === 'POST' && request.headers.has('authorization')
|
|
95
|
+
? Route.matchPath(service.routes, upstreamPath)
|
|
96
|
+
: null)
|
|
97
|
+
if (!matched) return new Response('Not Found', { status: 404 })
|
|
98
|
+
|
|
99
|
+
const endpoint = matched.value as Service.Endpoint
|
|
100
|
+
const ctx: Service.Context = { request, service, upstreamPath }
|
|
101
|
+
|
|
102
|
+
if (endpoint === true) return proxyUpstream({ request, service, ctx, proxy })
|
|
103
|
+
|
|
104
|
+
const handler = typeof endpoint === 'function' ? endpoint : endpoint.pay
|
|
105
|
+
const result = await handler(request)
|
|
106
|
+
if (result.status === 402) return result.challenge
|
|
107
|
+
|
|
108
|
+
const options = Service.getOptions(endpoint)
|
|
109
|
+
const upstreamRes = await proxyUpstream({
|
|
110
|
+
request,
|
|
111
|
+
service,
|
|
112
|
+
ctx: { ...ctx, ...options },
|
|
113
|
+
proxy,
|
|
114
|
+
})
|
|
115
|
+
return result.withReceipt(upstreamRes)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
fetch: handle,
|
|
120
|
+
listener: Request.toNodeListener(handle),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export declare namespace create {
|
|
125
|
+
export type Config = {
|
|
126
|
+
/** Base path prefix to strip before routing (e.g. `'/api/proxy'`). */
|
|
127
|
+
basePath?: string | undefined
|
|
128
|
+
/** Custom `fetch` implementation. Defaults to `globalThis.fetch`. */
|
|
129
|
+
fetch?: typeof globalThis.fetch | undefined
|
|
130
|
+
/** Services to proxy. Each service is mounted at `/{serviceId}/`. */
|
|
131
|
+
services: Service.Service[]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
declare namespace proxyUpstream {
|
|
136
|
+
type Options = {
|
|
137
|
+
ctx: Service.Context
|
|
138
|
+
proxy: (input: URL | RequestInfo, init?: RequestInit) => Promise<Response>
|
|
139
|
+
request: globalThis.Request
|
|
140
|
+
service: Service.Service
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** @internal */
|
|
145
|
+
async function proxyUpstream(options: proxyUpstream.Options): Promise<Response> {
|
|
146
|
+
const { request, service, ctx, proxy } = options
|
|
147
|
+
const url = ctx.upstreamPath + new URL(request.url).search
|
|
148
|
+
const headers = Headers.scrub(request.headers)
|
|
149
|
+
|
|
150
|
+
const method = request.method.toUpperCase()
|
|
151
|
+
const hasBody = method !== 'GET' && method !== 'HEAD'
|
|
152
|
+
|
|
153
|
+
const init: RequestInit & { duplex?: 'half' } = {
|
|
154
|
+
method: request.method,
|
|
155
|
+
headers,
|
|
156
|
+
signal: request.signal,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (hasBody && request.body) {
|
|
160
|
+
init.body = request.body
|
|
161
|
+
init.duplex = 'half'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let upstreamReq = new globalThis.Request(new URL(url, new URL(service.baseUrl).origin), init)
|
|
165
|
+
|
|
166
|
+
if (service.rewriteRequest) upstreamReq = await service.rewriteRequest(upstreamReq, ctx)
|
|
167
|
+
|
|
168
|
+
let upstreamRes = await proxy(upstreamReq)
|
|
169
|
+
|
|
170
|
+
upstreamRes = Headers.scrubResponse(upstreamRes)
|
|
171
|
+
|
|
172
|
+
if (service.rewriteResponse) upstreamRes = await service.rewriteResponse(upstreamRes, ctx)
|
|
173
|
+
|
|
174
|
+
return upstreamRes
|
|
175
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import * as Service from './Service.js'
|
|
3
|
+
|
|
4
|
+
describe('from', () => {
|
|
5
|
+
test('behavior: creates service with id and baseUrl', () => {
|
|
6
|
+
const service = Service.from('api', {
|
|
7
|
+
baseUrl: 'https://api.example.com',
|
|
8
|
+
routes: { 'GET /v1/status': true },
|
|
9
|
+
})
|
|
10
|
+
expect(service.id).toBe('api')
|
|
11
|
+
expect(service.baseUrl).toBe('https://api.example.com')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('behavior: bearer sets Authorization header', async () => {
|
|
15
|
+
const service = Service.from('api', {
|
|
16
|
+
baseUrl: 'https://api.example.com',
|
|
17
|
+
bearer: 'sk-123',
|
|
18
|
+
routes: { 'GET /v1/data': true },
|
|
19
|
+
})
|
|
20
|
+
const req = new Request('https://example.com')
|
|
21
|
+
const ctx = { request: req, service, upstreamPath: '/v1/data' }
|
|
22
|
+
const result = await service.rewriteRequest!(req, ctx)
|
|
23
|
+
expect(result.headers.get('authorization')).toBe('Bearer sk-123')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('behavior: headers sets custom headers', async () => {
|
|
27
|
+
const service = Service.from('api', {
|
|
28
|
+
baseUrl: 'https://api.example.com',
|
|
29
|
+
headers: { 'X-Api-Key': 'secret' },
|
|
30
|
+
routes: { 'GET /v1/data': true },
|
|
31
|
+
})
|
|
32
|
+
const req = new Request('https://example.com')
|
|
33
|
+
const ctx = { request: req, service, upstreamPath: '/v1/data' }
|
|
34
|
+
const result = await service.rewriteRequest!(req, ctx)
|
|
35
|
+
expect(result.headers.get('x-api-key')).toBe('secret')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('behavior: multiple headers sets all headers', async () => {
|
|
39
|
+
const service = Service.from('api', {
|
|
40
|
+
baseUrl: 'https://api.example.com',
|
|
41
|
+
headers: { 'X-Key': 'a', 'X-Secret': 'b' },
|
|
42
|
+
routes: { 'GET /v1/data': true },
|
|
43
|
+
})
|
|
44
|
+
const req = new Request('https://example.com')
|
|
45
|
+
const ctx = { request: req, service, upstreamPath: '/v1/data' }
|
|
46
|
+
const result = await service.rewriteRequest!(req, ctx)
|
|
47
|
+
expect(result.headers.get('x-key')).toBe('a')
|
|
48
|
+
expect(result.headers.get('x-secret')).toBe('b')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('behavior: mutate calls mutate function', async () => {
|
|
52
|
+
const service = Service.from('api', {
|
|
53
|
+
baseUrl: 'https://api.example.com',
|
|
54
|
+
mutate: (req) => {
|
|
55
|
+
req.headers.set('X-Mutated', 'yes')
|
|
56
|
+
return req
|
|
57
|
+
},
|
|
58
|
+
routes: { 'GET /v1/data': true },
|
|
59
|
+
})
|
|
60
|
+
const req = new Request('https://example.com')
|
|
61
|
+
const ctx = { request: req, service, upstreamPath: '/v1/data' }
|
|
62
|
+
const result = await service.rewriteRequest!(req, ctx)
|
|
63
|
+
expect(result.headers.get('x-mutated')).toBe('yes')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('behavior: no auth config means no rewriteRequest', () => {
|
|
67
|
+
const service = Service.from('api', {
|
|
68
|
+
baseUrl: 'https://api.example.com',
|
|
69
|
+
routes: { 'GET /v1/data': true },
|
|
70
|
+
})
|
|
71
|
+
expect(service.rewriteRequest).toBeUndefined()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('behavior: per-endpoint options override service bearer', async () => {
|
|
75
|
+
const handler: Service.IntentHandler = async () => ({
|
|
76
|
+
status: 200 as const,
|
|
77
|
+
withReceipt: <T>(r: T) => r,
|
|
78
|
+
})
|
|
79
|
+
const service = Service.from('api', {
|
|
80
|
+
baseUrl: 'https://api.example.com',
|
|
81
|
+
bearer: 'sk-default',
|
|
82
|
+
routes: {
|
|
83
|
+
'GET /v1/data': {
|
|
84
|
+
pay: handler,
|
|
85
|
+
options: { bearer: 'sk-override' },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
const endpoint = service.routes['GET /v1/data']!
|
|
90
|
+
const options = Service.getOptions(endpoint)
|
|
91
|
+
const req = new Request('https://example.com')
|
|
92
|
+
const ctx = { request: req, service, upstreamPath: '/v1/data', ...options }
|
|
93
|
+
const result = await service.rewriteRequest!(req, ctx)
|
|
94
|
+
expect(result.headers.get('authorization')).toBe('Bearer sk-override')
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe('custom', () => {
|
|
99
|
+
test('behavior: alias for from', () => {
|
|
100
|
+
expect(Service.custom).toBe(Service.from)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe('getOptions', () => {
|
|
105
|
+
test('behavior: returns options from endpoint object', () => {
|
|
106
|
+
const handler: Service.IntentHandler = async () => ({
|
|
107
|
+
status: 200 as const,
|
|
108
|
+
withReceipt: <T>(r: T) => r,
|
|
109
|
+
})
|
|
110
|
+
const endpoint: Service.Endpoint = { pay: handler, options: { apiKey: 'sk-123' } }
|
|
111
|
+
expect(Service.getOptions(endpoint)).toEqual({ apiKey: 'sk-123' })
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('behavior: returns undefined for function endpoint', () => {
|
|
115
|
+
const handler: Service.IntentHandler = async () => ({
|
|
116
|
+
status: 200 as const,
|
|
117
|
+
withReceipt: <T>(r: T) => r,
|
|
118
|
+
})
|
|
119
|
+
expect(Service.getOptions(handler)).toBeUndefined()
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('behavior: returns undefined for true endpoint', () => {
|
|
123
|
+
expect(Service.getOptions(true)).toBeUndefined()
|
|
124
|
+
})
|
|
125
|
+
})
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Value } from 'ox'
|
|
2
|
+
|
|
3
|
+
/** A proxied upstream service with route definitions and optional request/response hooks. */
|
|
4
|
+
export type Service = {
|
|
5
|
+
/** Unique identifier used as the URL prefix (e.g. `'openai'` → `/{id}/...`). */
|
|
6
|
+
id: string
|
|
7
|
+
/** Base URL of the upstream service (e.g. `'https://api.openai.com'`). */
|
|
8
|
+
baseUrl: string
|
|
9
|
+
/** Map of route patterns to endpoint handlers. */
|
|
10
|
+
routes: EndpointMap
|
|
11
|
+
/** Hook to modify the upstream request before sending (e.g. inject auth headers). */
|
|
12
|
+
rewriteRequest?: ((req: Request, ctx: Context) => Request | Promise<Request>) | undefined
|
|
13
|
+
/** Hook to modify the upstream response before returning to the client. */
|
|
14
|
+
rewriteResponse?: ((res: Response, ctx: Context) => Response | Promise<Response>) | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An endpoint definition.
|
|
19
|
+
*
|
|
20
|
+
* - `IntentHandler` — payment required, calls the handler to issue a 402 challenge or verify payment.
|
|
21
|
+
* - `{ pay, options }` — payment required with per-endpoint config overrides.
|
|
22
|
+
* - `true` — free passthrough, no payment required, rewriteRequest is applied.
|
|
23
|
+
*/
|
|
24
|
+
export type Endpoint = IntentHandler | { pay: IntentHandler; options: EndpointOptions } | true
|
|
25
|
+
|
|
26
|
+
/** Map of `"METHOD /pattern"` keys to endpoint definitions. */
|
|
27
|
+
export type EndpointMap<routes extends string = string> = Partial<Record<routes, Endpoint>> &
|
|
28
|
+
Record<string & {}, Endpoint>
|
|
29
|
+
|
|
30
|
+
/** Per-endpoint configuration overrides (e.g. `{ apiKey: 'sk-...' }`). */
|
|
31
|
+
export type EndpointOptions = {
|
|
32
|
+
[key: string]: unknown
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A function that handles the mppx payment flow for a request. */
|
|
36
|
+
export type IntentHandler = (input: Request) => Promise<IntentResult>
|
|
37
|
+
|
|
38
|
+
/** Result of an intent handler — either a 402 challenge or a 200 with receipt attachment. */
|
|
39
|
+
export type IntentResult =
|
|
40
|
+
| { challenge: Response; status: 402 }
|
|
41
|
+
| { status: 200; withReceipt: <response>(response: response) => response }
|
|
42
|
+
|
|
43
|
+
/** Context passed to `rewriteRequest`/`rewriteResponse` hooks, including any per-endpoint options. */
|
|
44
|
+
export type Context = {
|
|
45
|
+
request: Request
|
|
46
|
+
service: Service
|
|
47
|
+
upstreamPath: string
|
|
48
|
+
} & EndpointOptions
|
|
49
|
+
|
|
50
|
+
export type From<
|
|
51
|
+
options extends {
|
|
52
|
+
routes: string
|
|
53
|
+
},
|
|
54
|
+
> = {
|
|
55
|
+
routes: EndpointMap<options['routes']>
|
|
56
|
+
} & Omit<options, 'routes'>
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a service definition.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* Service.from('my-api', {
|
|
64
|
+
* baseUrl: 'https://api.example.com',
|
|
65
|
+
* bearer: 'sk-...',
|
|
66
|
+
* routes: {
|
|
67
|
+
* 'POST /v1/generate': mppx.charge({ amount: '0.01' }),
|
|
68
|
+
* 'GET /v1/status': true,
|
|
69
|
+
* },
|
|
70
|
+
* })
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function from<options = unknown>(id: string, config: from.Config<options>): Service {
|
|
74
|
+
const rewriteFromConfig = resolveRewriteRequest(config)
|
|
75
|
+
return {
|
|
76
|
+
id,
|
|
77
|
+
baseUrl: config.baseUrl,
|
|
78
|
+
routes: config.routes,
|
|
79
|
+
rewriteRequest: config.rewriteRequest
|
|
80
|
+
? rewriteFromConfig
|
|
81
|
+
? async (req, ctx) => {
|
|
82
|
+
req = await rewriteFromConfig(req, ctx)
|
|
83
|
+
return (config.rewriteRequest as Service['rewriteRequest'])!(req, ctx)
|
|
84
|
+
}
|
|
85
|
+
: (config.rewriteRequest as Service['rewriteRequest'])
|
|
86
|
+
: rewriteFromConfig,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export declare namespace from {
|
|
91
|
+
export type Config<options = unknown> = {
|
|
92
|
+
/** Base URL of the upstream service. */
|
|
93
|
+
baseUrl: string
|
|
94
|
+
/** Shorthand: inject `Authorization: Bearer {token}` header. */
|
|
95
|
+
bearer?: string | undefined
|
|
96
|
+
/** Shorthand: inject custom headers. */
|
|
97
|
+
headers?: Record<string, string> | undefined
|
|
98
|
+
/** Shorthand: full request mutation function. Takes priority over `bearer`/`headers`. */
|
|
99
|
+
mutate?: ((req: Request) => Request | Promise<Request>) | undefined
|
|
100
|
+
/** Hook to modify the upstream request. Receives typed per-endpoint options via `ctx`. */
|
|
101
|
+
rewriteRequest?:
|
|
102
|
+
| ((req: Request, ctx: Context & Partial<options & {}>) => Request | Promise<Request>)
|
|
103
|
+
| undefined
|
|
104
|
+
/** Map of route patterns to endpoint definitions. */
|
|
105
|
+
routes: EndpointMap
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { from as custom }
|
|
110
|
+
|
|
111
|
+
function resolveRewriteRequest(
|
|
112
|
+
config: from.Config,
|
|
113
|
+
): ((req: Request, ctx: Context) => Request | Promise<Request>) | undefined {
|
|
114
|
+
if (config.mutate) {
|
|
115
|
+
const mutate = config.mutate
|
|
116
|
+
return (req, ctx) => {
|
|
117
|
+
const options = ctx as Partial<from.Config>
|
|
118
|
+
const m = options.mutate ?? mutate
|
|
119
|
+
return m(req)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (config.bearer) {
|
|
123
|
+
const bearer = config.bearer
|
|
124
|
+
return (req, ctx) => {
|
|
125
|
+
const options = ctx as Partial<from.Config>
|
|
126
|
+
req.headers.set('Authorization', `Bearer ${options.bearer ?? bearer}`)
|
|
127
|
+
return req
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (config.headers) {
|
|
131
|
+
const headers = config.headers
|
|
132
|
+
return (req, ctx) => {
|
|
133
|
+
const options = ctx as Partial<from.Config>
|
|
134
|
+
const h = options.headers ?? headers
|
|
135
|
+
for (const [name, value] of Object.entries(h)) req.headers.set(name, value)
|
|
136
|
+
return req
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return undefined
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Serializes a service for discovery responses. */
|
|
143
|
+
export function serialize(s: Service) {
|
|
144
|
+
return {
|
|
145
|
+
id: s.id,
|
|
146
|
+
baseUrl: s.baseUrl,
|
|
147
|
+
routes: Object.entries(s.routes).map(([pattern, endpoint]) => {
|
|
148
|
+
const tokens = pattern.trim().split(/\s+/)
|
|
149
|
+
const hasMethod = tokens.length >= 2
|
|
150
|
+
return {
|
|
151
|
+
method: hasMethod ? tokens[0] : undefined,
|
|
152
|
+
path: hasMethod ? tokens.slice(1).join(' ') : tokens[0],
|
|
153
|
+
pattern,
|
|
154
|
+
payment: endpoint ? resolvePayment(endpoint) : null,
|
|
155
|
+
}
|
|
156
|
+
}),
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Renders an llms.txt markdown string for a list of services. */
|
|
161
|
+
export function toLlmsTxt(services: Service[]): string {
|
|
162
|
+
const lines: string[] = [
|
|
163
|
+
'# API Proxy',
|
|
164
|
+
'',
|
|
165
|
+
'> Paid API proxy powered by [Machine Payments Protocol](https://mpp.tempo.xyz).',
|
|
166
|
+
'',
|
|
167
|
+
'For machine-readable service data, use `GET /services` (JSON).',
|
|
168
|
+
'',
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
if (services.length === 0) return lines.join('\n')
|
|
172
|
+
|
|
173
|
+
lines.push('## Services', '')
|
|
174
|
+
for (const s of services) {
|
|
175
|
+
const serialized = serialize(s)
|
|
176
|
+
const free = serialized.routes.filter((r) => r.payment === null).length
|
|
177
|
+
const paid = serialized.routes.length - free
|
|
178
|
+
const parts = [paid && `${paid} paid`, free && `${free} free`].filter(Boolean).join(', ')
|
|
179
|
+
lines.push(`- [${s.id}](${s.baseUrl}): ${parts}`)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const s of services) {
|
|
183
|
+
const serialized = serialize(s)
|
|
184
|
+
lines.push('', `## ${s.id}`, '')
|
|
185
|
+
for (const route of serialized.routes) {
|
|
186
|
+
if (!route.payment) {
|
|
187
|
+
lines.push(`- \`${route.pattern}\`: Free`)
|
|
188
|
+
continue
|
|
189
|
+
}
|
|
190
|
+
const p = route.payment as Record<string, unknown>
|
|
191
|
+
const parts = [`${p.intent}`]
|
|
192
|
+
if (p.amount) {
|
|
193
|
+
const unit = `${p.amount} units`
|
|
194
|
+
parts.push(p.unitType ? `${unit} per ${p.unitType}` : unit)
|
|
195
|
+
}
|
|
196
|
+
if (p.description) parts.push(`"${p.description}"`)
|
|
197
|
+
const meta = [
|
|
198
|
+
p.currency && `currency: ${p.currency}`,
|
|
199
|
+
p.decimals !== undefined && `decimals: ${p.decimals}`,
|
|
200
|
+
].filter(Boolean)
|
|
201
|
+
if (meta.length) parts.push(`(${meta.join(', ')})`)
|
|
202
|
+
lines.push(`- \`${route.pattern}\`: ${parts.join(' — ')}`)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return lines.join('\n')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Extracts per-endpoint options from an endpoint definition. */
|
|
210
|
+
export function getOptions(endpoint: Endpoint): EndpointOptions | undefined {
|
|
211
|
+
if (typeof endpoint === 'object' && endpoint !== null && 'options' in endpoint)
|
|
212
|
+
return endpoint.options
|
|
213
|
+
return undefined
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function resolvePayment(endpoint: Endpoint): Record<string, unknown> | null {
|
|
217
|
+
if (endpoint === true) return null
|
|
218
|
+
const handler = typeof endpoint === 'function' ? endpoint : endpoint.pay
|
|
219
|
+
if (!('_internal' in handler)) return {}
|
|
220
|
+
const { name, method, defaults, schema, ...rest } = handler._internal as Record<string, unknown>
|
|
221
|
+
const amount = (() => {
|
|
222
|
+
if (typeof rest.amount === 'string' && typeof rest.decimals === 'number')
|
|
223
|
+
return String(Value.from(rest.amount, rest.decimals))
|
|
224
|
+
return rest.amount
|
|
225
|
+
})()
|
|
226
|
+
return { intent: name, method, ...rest, ...(amount !== undefined && { amount }) }
|
|
227
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * as Proxy from './Proxy.js'
|
|
2
|
+
export * as Service from './Service.js'
|
|
3
|
+
export { from as custom } from './Service.js'
|
|
4
|
+
export { anthropic } from './services/anthropic.js'
|
|
5
|
+
export { openai } from './services/openai.js'
|
|
6
|
+
export { stripe } from './services/stripe.js'
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import * as Headers from './Headers.js'
|
|
3
|
+
|
|
4
|
+
describe('scrub', () => {
|
|
5
|
+
test('behavior: strips authorization header', () => {
|
|
6
|
+
const headers = new globalThis.Headers({
|
|
7
|
+
Authorization: 'Bearer secret',
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
})
|
|
10
|
+
const result = Headers.scrub(headers)
|
|
11
|
+
expect(result.has('authorization')).toBe(false)
|
|
12
|
+
expect(result.get('content-type')).toBe('application/json')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('behavior: strips cookie header', () => {
|
|
16
|
+
const headers = new globalThis.Headers({
|
|
17
|
+
Cookie: 'session=abc123',
|
|
18
|
+
Accept: 'text/html',
|
|
19
|
+
})
|
|
20
|
+
const result = Headers.scrub(headers)
|
|
21
|
+
expect(result.has('cookie')).toBe(false)
|
|
22
|
+
expect(result.get('accept')).toBe('text/html')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('behavior: strips hop-by-hop headers', () => {
|
|
26
|
+
const headers = new globalThis.Headers({
|
|
27
|
+
Connection: 'keep-alive',
|
|
28
|
+
'Keep-Alive': 'timeout=5',
|
|
29
|
+
'Transfer-Encoding': 'chunked',
|
|
30
|
+
Upgrade: 'websocket',
|
|
31
|
+
'Proxy-Authenticate': 'Basic',
|
|
32
|
+
'Proxy-Authorization': 'Basic abc',
|
|
33
|
+
TE: 'trailers',
|
|
34
|
+
Trailer: 'Expires',
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
})
|
|
37
|
+
const result = Headers.scrub(headers)
|
|
38
|
+
expect(result.has('connection')).toBe(false)
|
|
39
|
+
expect(result.has('keep-alive')).toBe(false)
|
|
40
|
+
expect(result.has('transfer-encoding')).toBe(false)
|
|
41
|
+
expect(result.has('upgrade')).toBe(false)
|
|
42
|
+
expect(result.has('proxy-authenticate')).toBe(false)
|
|
43
|
+
expect(result.has('proxy-authorization')).toBe(false)
|
|
44
|
+
expect(result.has('te')).toBe(false)
|
|
45
|
+
expect(result.has('trailer')).toBe(false)
|
|
46
|
+
expect(result.get('content-type')).toBe('application/json')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('behavior: strips x-forwarded-* headers', () => {
|
|
50
|
+
const headers = new globalThis.Headers({
|
|
51
|
+
'X-Forwarded-For': '127.0.0.1',
|
|
52
|
+
'X-Forwarded-Proto': 'https',
|
|
53
|
+
'X-Forwarded-Host': 'example.com',
|
|
54
|
+
Accept: '*/*',
|
|
55
|
+
})
|
|
56
|
+
const result = Headers.scrub(headers)
|
|
57
|
+
expect(result.has('x-forwarded-for')).toBe(false)
|
|
58
|
+
expect(result.has('x-forwarded-proto')).toBe(false)
|
|
59
|
+
expect(result.has('x-forwarded-host')).toBe(false)
|
|
60
|
+
expect(result.get('accept')).toBe('*/*')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('behavior: preserves safe headers', () => {
|
|
64
|
+
const headers = new globalThis.Headers({
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
Accept: 'text/html',
|
|
67
|
+
'User-Agent': 'test-agent/1.0',
|
|
68
|
+
'X-Custom-Header': 'value',
|
|
69
|
+
})
|
|
70
|
+
const result = Headers.scrub(headers)
|
|
71
|
+
expect(result.get('content-type')).toBe('application/json')
|
|
72
|
+
expect(result.get('accept')).toBe('text/html')
|
|
73
|
+
expect(result.get('user-agent')).toBe('test-agent/1.0')
|
|
74
|
+
expect(result.get('x-custom-header')).toBe('value')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('scrubResponse', () => {
|
|
79
|
+
test('behavior: strips content-encoding and content-length', () => {
|
|
80
|
+
const response = new Response('body', {
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Encoding': 'gzip',
|
|
83
|
+
'Content-Length': '42',
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
const result = Headers.scrubResponse(response)
|
|
88
|
+
expect(result.headers.has('content-encoding')).toBe(false)
|
|
89
|
+
expect(result.headers.has('content-length')).toBe(false)
|
|
90
|
+
expect(result.headers.get('content-type')).toBe('application/json')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('behavior: preserves status and body', async () => {
|
|
94
|
+
const response = new Response('hello', { status: 201, statusText: 'Created' })
|
|
95
|
+
const result = Headers.scrubResponse(response)
|
|
96
|
+
expect(result.status).toBe(201)
|
|
97
|
+
expect(result.statusText).toBe('Created')
|
|
98
|
+
expect(await result.text()).toBe('hello')
|
|
99
|
+
})
|
|
100
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const hopByHopHeaders = new Set([
|
|
2
|
+
'connection',
|
|
3
|
+
'keep-alive',
|
|
4
|
+
'transfer-encoding',
|
|
5
|
+
'upgrade',
|
|
6
|
+
'proxy-authenticate',
|
|
7
|
+
'proxy-authorization',
|
|
8
|
+
'te',
|
|
9
|
+
'trailer',
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
export function scrub(headers: Headers): Headers {
|
|
13
|
+
const scrubbed = new Headers()
|
|
14
|
+
|
|
15
|
+
for (const [name, value] of headers) {
|
|
16
|
+
const lower = name.toLowerCase()
|
|
17
|
+
|
|
18
|
+
if (lower === 'authorization') continue
|
|
19
|
+
if (lower === 'accept-encoding') continue
|
|
20
|
+
if (lower === 'content-length') continue
|
|
21
|
+
if (lower === 'cookie') continue
|
|
22
|
+
if (hopByHopHeaders.has(lower)) continue
|
|
23
|
+
if (lower.startsWith('x-forwarded-')) continue
|
|
24
|
+
|
|
25
|
+
scrubbed.append(name, value)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return scrubbed
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function scrubResponse(response: Response): Response {
|
|
32
|
+
const headers = new Headers(response.headers)
|
|
33
|
+
headers.delete('content-encoding')
|
|
34
|
+
headers.delete('content-length')
|
|
35
|
+
return new Response(response.body, {
|
|
36
|
+
status: response.status,
|
|
37
|
+
statusText: response.statusText,
|
|
38
|
+
headers,
|
|
39
|
+
})
|
|
40
|
+
}
|