@scriptmasterlabs/mcp-x402 2.0.2 → 2.1.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/.well-known/x402.json +37 -0
- package/LICENSE +57 -21
- package/README.md +262 -304
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-wrapper.d.ts +71 -0
- package/dist/mcp-wrapper.d.ts.map +1 -0
- package/dist/mcp-wrapper.js +104 -0
- package/dist/mcp-wrapper.js.map +1 -0
- package/dist/x402-middleware.d.ts +76 -0
- package/dist/x402-middleware.d.ts.map +1 -0
- package/dist/x402-middleware.js +113 -0
- package/dist/x402-middleware.js.map +1 -0
- package/dist/xrpl-facilitator.d.ts +77 -0
- package/dist/xrpl-facilitator.d.ts.map +1 -0
- package/dist/xrpl-facilitator.js +156 -0
- package/dist/xrpl-facilitator.js.map +1 -0
- package/llms.txt +108 -70
- package/package.json +65 -78
- package/schema.jsonld +97 -0
- package/.env.example +0 -35
- package/.github/workflows/ci.yml +0 -59
- package/.github/workflows/keepalive.yml +0 -31
- package/.well-known/agentcard.json +0 -34
- package/CONTRIBUTING.md +0 -76
- package/Dockerfile +0 -19
- package/agents.json +0 -67
- package/dist/lib/chains/base.d.ts +0 -10
- package/dist/lib/chains/base.d.ts.map +0 -1
- package/dist/lib/chains/base.js +0 -73
- package/dist/lib/chains/base.js.map +0 -1
- package/dist/lib/chains/solana.d.ts +0 -10
- package/dist/lib/chains/solana.d.ts.map +0 -1
- package/dist/lib/chains/solana.js +0 -49
- package/dist/lib/chains/solana.js.map +0 -1
- package/dist/lib/chains/xrpl.d.ts +0 -10
- package/dist/lib/chains/xrpl.d.ts.map +0 -1
- package/dist/lib/chains/xrpl.js +0 -55
- package/dist/lib/chains/xrpl.js.map +0 -1
- package/dist/lib/credit/bureau.d.ts +0 -10
- package/dist/lib/credit/bureau.d.ts.map +0 -1
- package/dist/lib/credit/bureau.js +0 -58
- package/dist/lib/credit/bureau.js.map +0 -1
- package/dist/lib/sml-api/agentcard.d.ts +0 -17
- package/dist/lib/sml-api/agentcard.d.ts.map +0 -1
- package/dist/lib/sml-api/agentcard.js +0 -30
- package/dist/lib/sml-api/agentcard.js.map +0 -1
- package/dist/lib/sml-api/backtest.d.ts +0 -22
- package/dist/lib/sml-api/backtest.d.ts.map +0 -1
- package/dist/lib/sml-api/backtest.js +0 -28
- package/dist/lib/sml-api/backtest.js.map +0 -1
- package/dist/lib/sml-api/brokers.d.ts +0 -40
- package/dist/lib/sml-api/brokers.d.ts.map +0 -1
- package/dist/lib/sml-api/brokers.js +0 -128
- package/dist/lib/sml-api/brokers.js.map +0 -1
- package/dist/lib/sml-api/copytrader.d.ts +0 -11
- package/dist/lib/sml-api/copytrader.d.ts.map +0 -1
- package/dist/lib/sml-api/copytrader.js +0 -30
- package/dist/lib/sml-api/copytrader.js.map +0 -1
- package/dist/lib/sml-api/crawl.d.ts +0 -20
- package/dist/lib/sml-api/crawl.d.ts.map +0 -1
- package/dist/lib/sml-api/crawl.js +0 -32
- package/dist/lib/sml-api/crawl.js.map +0 -1
- package/dist/lib/sml-api/echo.d.ts +0 -10
- package/dist/lib/sml-api/echo.d.ts.map +0 -1
- package/dist/lib/sml-api/echo.js +0 -23
- package/dist/lib/sml-api/echo.js.map +0 -1
- package/dist/lib/sml-api/forge.d.ts +0 -11
- package/dist/lib/sml-api/forge.d.ts.map +0 -1
- package/dist/lib/sml-api/forge.js +0 -29
- package/dist/lib/sml-api/forge.js.map +0 -1
- package/dist/lib/sml-api/ftd.d.ts +0 -18
- package/dist/lib/sml-api/ftd.d.ts.map +0 -1
- package/dist/lib/sml-api/ftd.js +0 -43
- package/dist/lib/sml-api/ftd.js.map +0 -1
- package/dist/lib/sml-api/ghost.d.ts +0 -13
- package/dist/lib/sml-api/ghost.d.ts.map +0 -1
- package/dist/lib/sml-api/ghost.js +0 -29
- package/dist/lib/sml-api/ghost.js.map +0 -1
- package/dist/lib/sml-api/launchpad.d.ts +0 -20
- package/dist/lib/sml-api/launchpad.d.ts.map +0 -1
- package/dist/lib/sml-api/launchpad.js +0 -31
- package/dist/lib/sml-api/launchpad.js.map +0 -1
- package/dist/lib/sml-api/leviathan.d.ts +0 -22
- package/dist/lib/sml-api/leviathan.d.ts.map +0 -1
- package/dist/lib/sml-api/leviathan.js +0 -33
- package/dist/lib/sml-api/leviathan.js.map +0 -1
- package/dist/lib/sml-api/nexus.d.ts +0 -18
- package/dist/lib/sml-api/nexus.d.ts.map +0 -1
- package/dist/lib/sml-api/nexus.js +0 -40
- package/dist/lib/sml-api/nexus.js.map +0 -1
- package/dist/lib/sml-api/proof402.d.ts +0 -6
- package/dist/lib/sml-api/proof402.d.ts.map +0 -1
- package/dist/lib/sml-api/proof402.js +0 -30
- package/dist/lib/sml-api/proof402.js.map +0 -1
- package/dist/lib/sml-api/rails.d.ts +0 -12
- package/dist/lib/sml-api/rails.d.ts.map +0 -1
- package/dist/lib/sml-api/rails.js +0 -29
- package/dist/lib/sml-api/rails.js.map +0 -1
- package/dist/lib/sml-api/shadow.d.ts +0 -15
- package/dist/lib/sml-api/shadow.d.ts.map +0 -1
- package/dist/lib/sml-api/shadow.js +0 -27
- package/dist/lib/sml-api/shadow.js.map +0 -1
- package/dist/lib/sml-api/squeezeos.d.ts +0 -21
- package/dist/lib/sml-api/squeezeos.d.ts.map +0 -1
- package/dist/lib/sml-api/squeezeos.js +0 -97
- package/dist/lib/sml-api/squeezeos.js.map +0 -1
- package/dist/lib/sml-api/xdeo.d.ts +0 -13
- package/dist/lib/sml-api/xdeo.d.ts.map +0 -1
- package/dist/lib/sml-api/xdeo.js +0 -34
- package/dist/lib/sml-api/xdeo.js.map +0 -1
- package/dist/lib/sml-api/xmit.d.ts +0 -13
- package/dist/lib/sml-api/xmit.d.ts.map +0 -1
- package/dist/lib/sml-api/xmit.js +0 -34
- package/dist/lib/sml-api/xmit.js.map +0 -1
- package/dist/server/health.d.ts +0 -16
- package/dist/server/health.d.ts.map +0 -1
- package/dist/server/health.js +0 -39
- package/dist/server/health.js.map +0 -1
- package/dist/server/index.d.ts +0 -3
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js +0 -199
- package/dist/server/index.js.map +0 -1
- package/dist/server/payments/ap2.d.ts +0 -17
- package/dist/server/payments/ap2.d.ts.map +0 -1
- package/dist/server/payments/ap2.js +0 -77
- package/dist/server/payments/ap2.js.map +0 -1
- package/dist/server/payments/receipt.d.ts +0 -28
- package/dist/server/payments/receipt.d.ts.map +0 -1
- package/dist/server/payments/receipt.js +0 -60
- package/dist/server/payments/receipt.js.map +0 -1
- package/dist/server/payments/router.d.ts +0 -23
- package/dist/server/payments/router.d.ts.map +0 -1
- package/dist/server/payments/router.js +0 -69
- package/dist/server/payments/router.js.map +0 -1
- package/dist/server/payments/wallet.d.ts +0 -18
- package/dist/server/payments/wallet.d.ts.map +0 -1
- package/dist/server/payments/wallet.js +0 -107
- package/dist/server/payments/wallet.js.map +0 -1
- package/dist/server/payments/x402.d.ts +0 -29
- package/dist/server/payments/x402.d.ts.map +0 -1
- package/dist/server/payments/x402.js +0 -138
- package/dist/server/payments/x402.js.map +0 -1
- package/dist/server/registry/catalog.d.ts +0 -12
- package/dist/server/registry/catalog.d.ts.map +0 -1
- package/dist/server/registry/catalog.js +0 -55
- package/dist/server/registry/catalog.js.map +0 -1
- package/dist/server/registry/discovery.d.ts +0 -16
- package/dist/server/registry/discovery.d.ts.map +0 -1
- package/dist/server/registry/discovery.js +0 -33
- package/dist/server/registry/discovery.js.map +0 -1
- package/dist/server/registry/pricing.d.ts +0 -10
- package/dist/server/registry/pricing.d.ts.map +0 -1
- package/dist/server/registry/pricing.js +0 -123
- package/dist/server/registry/pricing.js.map +0 -1
- package/dist/server/security/acl.d.ts +0 -28
- package/dist/server/security/acl.d.ts.map +0 -1
- package/dist/server/security/acl.js +0 -36
- package/dist/server/security/acl.js.map +0 -1
- package/dist/server/security/audit.d.ts +0 -15
- package/dist/server/security/audit.d.ts.map +0 -1
- package/dist/server/security/audit.js +0 -77
- package/dist/server/security/audit.js.map +0 -1
- package/dist/server/security/rate-limit.d.ts +0 -12
- package/dist/server/security/rate-limit.d.ts.map +0 -1
- package/dist/server/security/rate-limit.js +0 -72
- package/dist/server/security/rate-limit.js.map +0 -1
- package/dist/server/security/sandbox.d.ts +0 -7
- package/dist/server/security/sandbox.d.ts.map +0 -1
- package/dist/server/security/sandbox.js +0 -42
- package/dist/server/security/sandbox.js.map +0 -1
- package/dist/server/tools/agentcard.d.ts +0 -3
- package/dist/server/tools/agentcard.d.ts.map +0 -1
- package/dist/server/tools/agentcard.js +0 -118
- package/dist/server/tools/agentcard.js.map +0 -1
- package/dist/server/tools/backtest.d.ts +0 -3
- package/dist/server/tools/backtest.d.ts.map +0 -1
- package/dist/server/tools/backtest.js +0 -112
- package/dist/server/tools/backtest.js.map +0 -1
- package/dist/server/tools/brokers.d.ts +0 -3
- package/dist/server/tools/brokers.d.ts.map +0 -1
- package/dist/server/tools/brokers.js +0 -223
- package/dist/server/tools/brokers.js.map +0 -1
- package/dist/server/tools/copytrader.d.ts +0 -3
- package/dist/server/tools/copytrader.d.ts.map +0 -1
- package/dist/server/tools/copytrader.js +0 -90
- package/dist/server/tools/copytrader.js.map +0 -1
- package/dist/server/tools/crawl.d.ts +0 -3
- package/dist/server/tools/crawl.d.ts.map +0 -1
- package/dist/server/tools/crawl.js +0 -60
- package/dist/server/tools/crawl.js.map +0 -1
- package/dist/server/tools/discovery.d.ts +0 -3
- package/dist/server/tools/discovery.d.ts.map +0 -1
- package/dist/server/tools/discovery.js +0 -188
- package/dist/server/tools/discovery.js.map +0 -1
- package/dist/server/tools/echo.d.ts +0 -3
- package/dist/server/tools/echo.d.ts.map +0 -1
- package/dist/server/tools/echo.js +0 -48
- package/dist/server/tools/echo.js.map +0 -1
- package/dist/server/tools/forge.d.ts +0 -3
- package/dist/server/tools/forge.d.ts.map +0 -1
- package/dist/server/tools/forge.js +0 -77
- package/dist/server/tools/forge.js.map +0 -1
- package/dist/server/tools/ftd.d.ts +0 -3
- package/dist/server/tools/ftd.d.ts.map +0 -1
- package/dist/server/tools/ftd.js +0 -70
- package/dist/server/tools/ftd.js.map +0 -1
- package/dist/server/tools/ghost.d.ts +0 -3
- package/dist/server/tools/ghost.d.ts.map +0 -1
- package/dist/server/tools/ghost.js +0 -83
- package/dist/server/tools/ghost.js.map +0 -1
- package/dist/server/tools/index.d.ts +0 -3
- package/dist/server/tools/index.d.ts.map +0 -1
- package/dist/server/tools/index.js +0 -44
- package/dist/server/tools/index.js.map +0 -1
- package/dist/server/tools/launchpad.d.ts +0 -3
- package/dist/server/tools/launchpad.d.ts.map +0 -1
- package/dist/server/tools/launchpad.js +0 -151
- package/dist/server/tools/launchpad.js.map +0 -1
- package/dist/server/tools/leviathan.d.ts +0 -3
- package/dist/server/tools/leviathan.d.ts.map +0 -1
- package/dist/server/tools/leviathan.js +0 -73
- package/dist/server/tools/leviathan.js.map +0 -1
- package/dist/server/tools/nexus.d.ts +0 -3
- package/dist/server/tools/nexus.d.ts.map +0 -1
- package/dist/server/tools/nexus.js +0 -65
- package/dist/server/tools/nexus.js.map +0 -1
- package/dist/server/tools/proof402.d.ts +0 -3
- package/dist/server/tools/proof402.d.ts.map +0 -1
- package/dist/server/tools/proof402.js +0 -74
- package/dist/server/tools/proof402.js.map +0 -1
- package/dist/server/tools/rails.d.ts +0 -3
- package/dist/server/tools/rails.d.ts.map +0 -1
- package/dist/server/tools/rails.js +0 -82
- package/dist/server/tools/rails.js.map +0 -1
- package/dist/server/tools/shadow.d.ts +0 -3
- package/dist/server/tools/shadow.d.ts.map +0 -1
- package/dist/server/tools/shadow.js +0 -114
- package/dist/server/tools/shadow.js.map +0 -1
- package/dist/server/tools/squeezeos.d.ts +0 -3
- package/dist/server/tools/squeezeos.d.ts.map +0 -1
- package/dist/server/tools/squeezeos.js +0 -231
- package/dist/server/tools/squeezeos.js.map +0 -1
- package/dist/server/tools/xdeo.d.ts +0 -3
- package/dist/server/tools/xdeo.d.ts.map +0 -1
- package/dist/server/tools/xdeo.js +0 -58
- package/dist/server/tools/xdeo.js.map +0 -1
- package/dist/server/tools/xmit.d.ts +0 -3
- package/dist/server/tools/xmit.d.ts.map +0 -1
- package/dist/server/tools/xmit.js +0 -59
- package/dist/server/tools/xmit.js.map +0 -1
- package/docker-compose.yml +0 -50
- package/mcp-publisher.exe +0 -0
- package/render.yaml +0 -39
- package/sdk/mcp-x402-sdk/package.json +0 -18
- package/sdk/mcp-x402-sdk/src/index.ts +0 -118
- package/sdk/mcp-x402-sdk/tsconfig.json +0 -14
- package/server.json +0 -48
- package/services/backtest_service.py +0 -176
- package/src/lib/chains/base.ts +0 -77
- package/src/lib/chains/solana.ts +0 -59
- package/src/lib/chains/xrpl.ts +0 -63
- package/src/lib/credit/bureau.ts +0 -65
- package/src/lib/sml-api/agentcard.ts +0 -40
- package/src/lib/sml-api/backtest.ts +0 -47
- package/src/lib/sml-api/brokers.ts +0 -160
- package/src/lib/sml-api/copytrader.ts +0 -33
- package/src/lib/sml-api/crawl.ts +0 -44
- package/src/lib/sml-api/echo.ts +0 -28
- package/src/lib/sml-api/forge.ts +0 -33
- package/src/lib/sml-api/ftd.ts +0 -53
- package/src/lib/sml-api/ghost.ts +0 -35
- package/src/lib/sml-api/launchpad.ts +0 -43
- package/src/lib/sml-api/leviathan.ts +0 -49
- package/src/lib/sml-api/nexus.ts +0 -50
- package/src/lib/sml-api/proof402.ts +0 -27
- package/src/lib/sml-api/rails.ts +0 -34
- package/src/lib/sml-api/shadow.ts +0 -35
- package/src/lib/sml-api/squeezeos.ts +0 -95
- package/src/lib/sml-api/xdeo.ts +0 -40
- package/src/lib/sml-api/xmit.ts +0 -40
- package/src/server/health.ts +0 -52
- package/src/server/index.ts +0 -213
- package/src/server/payments/ap2.ts +0 -101
- package/src/server/payments/receipt.ts +0 -85
- package/src/server/payments/router.ts +0 -110
- package/src/server/payments/wallet.ts +0 -123
- package/src/server/payments/x402.ts +0 -177
- package/src/server/registry/catalog.ts +0 -61
- package/src/server/registry/discovery.ts +0 -39
- package/src/server/registry/pricing.ts +0 -133
- package/src/server/security/acl.ts +0 -42
- package/src/server/security/audit.ts +0 -94
- package/src/server/security/rate-limit.ts +0 -84
- package/src/server/security/sandbox.ts +0 -40
- package/src/server/tools/agentcard.ts +0 -134
- package/src/server/tools/backtest.ts +0 -119
- package/src/server/tools/brokers.ts +0 -250
- package/src/server/tools/copytrader.ts +0 -104
- package/src/server/tools/crawl.ts +0 -70
- package/src/server/tools/discovery.ts +0 -202
- package/src/server/tools/echo.ts +0 -58
- package/src/server/tools/forge.ts +0 -87
- package/src/server/tools/ftd.ts +0 -88
- package/src/server/tools/ghost.ts +0 -93
- package/src/server/tools/index.ts +0 -42
- package/src/server/tools/launchpad.ts +0 -173
- package/src/server/tools/leviathan.ts +0 -81
- package/src/server/tools/nexus.ts +0 -76
- package/src/server/tools/proof402.ts +0 -87
- package/src/server/tools/rails.ts +0 -92
- package/src/server/tools/shadow.ts +0 -128
- package/src/server/tools/squeezeos.ts +0 -312
- package/src/server/tools/xdeo.ts +0 -67
- package/src/server/tools/xmit.ts +0 -68
- package/tests/integration/e2e.test.ts +0 -51
- package/tests/unit/payments.test.ts +0 -49
- package/tests/unit/security.test.ts +0 -92
- package/tests/unit/tools.test.ts +0 -42
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -20
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const SHADOW_BASE = process.env['SHADOW_DESK_URL'] ?? 'https://shadow-desk.onrender.com';
|
|
2
|
-
const SHADOW_ADMIN_KEY = process.env['SHADOW_ADMIN_API_KEY'] ?? '';
|
|
3
|
-
|
|
4
|
-
async function shadowPost(path: string, body: unknown): Promise<unknown> {
|
|
5
|
-
const headers: Record<string, string> = {
|
|
6
|
-
'Accept': 'application/json',
|
|
7
|
-
'Content-Type': 'application/json',
|
|
8
|
-
};
|
|
9
|
-
if (SHADOW_ADMIN_KEY) headers['X-Admin-Key'] = SHADOW_ADMIN_KEY;
|
|
10
|
-
const res = await fetch(`${SHADOW_BASE}${path}`, {
|
|
11
|
-
method: 'POST',
|
|
12
|
-
headers,
|
|
13
|
-
body: JSON.stringify(body),
|
|
14
|
-
signal: AbortSignal.timeout(15_000),
|
|
15
|
-
});
|
|
16
|
-
if (!res.ok) throw new Error(`Shadow Desk POST ${path}: HTTP ${res.status}`);
|
|
17
|
-
return res.json();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ShadowQueryParams {
|
|
21
|
-
query: string;
|
|
22
|
-
context?: string;
|
|
23
|
-
walletAddress: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ShadowIngestParams {
|
|
27
|
-
source: string;
|
|
28
|
-
payload: unknown;
|
|
29
|
-
walletAddress: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const ShadowDeskAPI = {
|
|
33
|
-
query: (params: ShadowQueryParams) => shadowPost('/query', params),
|
|
34
|
-
ingest: (params: ShadowIngestParams) => shadowPost('/ingest', params),
|
|
35
|
-
};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { createHmac } from 'crypto';
|
|
2
|
-
|
|
3
|
-
const SQUEEZEOS_BASE = process.env['SQUEEZEOS_BASE_URL'] ?? 'https://squeezeos-api.onrender.com';
|
|
4
|
-
const PROOF402_SECRET = process.env['PROOF402_TOKEN_SECRET'] ?? '';
|
|
5
|
-
|
|
6
|
-
// Endpoint UUIDs — override via env vars if they change
|
|
7
|
-
const ENDPOINT_UUIDS: Record<string, string> = {
|
|
8
|
-
council: process.env['SQUEEZEOS_UUID_COUNCIL'] ?? '12a0e7a1-0000-0000-0000-000000000001',
|
|
9
|
-
scan: process.env['SQUEEZEOS_UUID_SCAN'] ?? '160cf28d-0000-0000-0000-000000000002',
|
|
10
|
-
options: process.env['SQUEEZEOS_UUID_OPTIONS'] ?? 'c951a374-0000-0000-0000-000000000003',
|
|
11
|
-
iwm: process.env['SQUEEZEOS_UUID_IWM'] ?? '60f48ce0-0000-0000-0000-000000000004',
|
|
12
|
-
marketplace_read: process.env['SQUEEZEOS_UUID_MARKETPLACE_READ'] ?? 'd1a2b3c4-0000-0000-0000-000000000005',
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Generate a SqueezeOS JWT for a premium endpoint.
|
|
17
|
-
* Format: base64(json).HMAC-SHA256(secret, encoded)
|
|
18
|
-
*/
|
|
19
|
-
export function generateSqueezeOSToken(endpointKey: string, walletAddress: string): string {
|
|
20
|
-
const eid = ENDPOINT_UUIDS[endpointKey];
|
|
21
|
-
if (!eid) throw new Error(`Unknown SqueezeOS endpoint key: ${endpointKey}`);
|
|
22
|
-
if (!PROOF402_SECRET) throw new Error('PROOF402_TOKEN_SECRET is not set — cannot mint SqueezeOS JWT');
|
|
23
|
-
|
|
24
|
-
const payload = {
|
|
25
|
-
eid,
|
|
26
|
-
wlt: walletAddress,
|
|
27
|
-
iid: `mcp-x402-${Date.now()}`,
|
|
28
|
-
exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const encoded = Buffer.from(JSON.stringify(payload)).toString('base64url');
|
|
32
|
-
const sig = createHmac('sha256', PROOF402_SECRET).update(encoded).digest('hex');
|
|
33
|
-
return `${encoded}.${sig}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function squeezeGet(path: string, token?: string): Promise<unknown> {
|
|
37
|
-
const headers: Record<string, string> = { 'Accept': 'application/json' };
|
|
38
|
-
if (token) headers['X-Payment-Token'] = token;
|
|
39
|
-
const res = await fetch(`${SQUEEZEOS_BASE}${path}`, {
|
|
40
|
-
headers,
|
|
41
|
-
signal: AbortSignal.timeout(15_000),
|
|
42
|
-
});
|
|
43
|
-
if (!res.ok) throw new Error(`SqueezeOS GET ${path}: HTTP ${res.status}`);
|
|
44
|
-
return res.json();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function squeezePost(path: string, body: unknown, token?: string): Promise<unknown> {
|
|
48
|
-
const headers: Record<string, string> = {
|
|
49
|
-
'Accept': 'application/json',
|
|
50
|
-
'Content-Type': 'application/json',
|
|
51
|
-
};
|
|
52
|
-
if (token) headers['X-Payment-Token'] = token;
|
|
53
|
-
const res = await fetch(`${SQUEEZEOS_BASE}${path}`, {
|
|
54
|
-
method: 'POST',
|
|
55
|
-
headers,
|
|
56
|
-
body: JSON.stringify(body),
|
|
57
|
-
signal: AbortSignal.timeout(15_000),
|
|
58
|
-
});
|
|
59
|
-
if (!res.ok) throw new Error(`SqueezeOS POST ${path}: HTTP ${res.status}`);
|
|
60
|
-
return res.json();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const SqueezeOSAPI = {
|
|
64
|
-
// FREE
|
|
65
|
-
preview: (symbol: string) => squeezeGet(`/api/preview/${encodeURIComponent(symbol)}`),
|
|
66
|
-
history: (symbol?: string) => squeezeGet(symbol ? `/api/history/${encodeURIComponent(symbol)}` : '/api/history'),
|
|
67
|
-
oracle: (symbol?: string) => squeezeGet(symbol ? `/api/oracle/${encodeURIComponent(symbol)}` : '/api/oracle'),
|
|
68
|
-
ftd: () => squeezeGet('/api/ftd'),
|
|
69
|
-
status: () => squeezeGet('/api/status'),
|
|
70
|
-
demo: () => squeezeGet('/api/demo'),
|
|
71
|
-
marketplaceBrowse: () => squeezeGet('/api/marketplace'),
|
|
72
|
-
futuresLeaderboard: () => squeezeGet('/api/futures/leaderboard'),
|
|
73
|
-
|
|
74
|
-
// PAID — caller must supply walletAddress so we can mint the token
|
|
75
|
-
council: (symbol: string, walletAddress: string) => {
|
|
76
|
-
const token = generateSqueezeOSToken('council', walletAddress);
|
|
77
|
-
return squeezePost('/api/council', { symbol }, token);
|
|
78
|
-
},
|
|
79
|
-
scan: (walletAddress: string) => {
|
|
80
|
-
const token = generateSqueezeOSToken('scan', walletAddress);
|
|
81
|
-
return squeezeGet('/api/scan', token);
|
|
82
|
-
},
|
|
83
|
-
options: (walletAddress: string) => {
|
|
84
|
-
const token = generateSqueezeOSToken('options', walletAddress);
|
|
85
|
-
return squeezeGet('/api/options', token);
|
|
86
|
-
},
|
|
87
|
-
iwm: (walletAddress: string) => {
|
|
88
|
-
const token = generateSqueezeOSToken('iwm', walletAddress);
|
|
89
|
-
return squeezeGet('/api/iwm', token);
|
|
90
|
-
},
|
|
91
|
-
marketplaceRead: (listingId: string, walletAddress: string) => {
|
|
92
|
-
const token = generateSqueezeOSToken('marketplace_read', walletAddress);
|
|
93
|
-
return squeezePost('/api/marketplace/read', { listing_id: listingId }, token);
|
|
94
|
-
},
|
|
95
|
-
};
|
package/src/lib/sml-api/xdeo.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export interface XdeoEstimateParams {
|
|
2
|
-
ticker: string;
|
|
3
|
-
fiscalQuarter: string;
|
|
4
|
-
estimateType: 'eps' | 'revenue' | 'guidance' | 'all';
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export class XdeoClient {
|
|
8
|
-
private static instance: XdeoClient;
|
|
9
|
-
private readonly baseUrl: string;
|
|
10
|
-
|
|
11
|
-
private constructor() {
|
|
12
|
-
this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
static getInstance(): XdeoClient {
|
|
16
|
-
if (!XdeoClient.instance) {
|
|
17
|
-
XdeoClient.instance = new XdeoClient();
|
|
18
|
-
}
|
|
19
|
-
return XdeoClient.instance;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async getEstimate(params: XdeoEstimateParams): Promise<unknown> {
|
|
23
|
-
const res = await fetch(`${this.baseUrl}/xdeo/v1/estimate`, {
|
|
24
|
-
method: 'POST',
|
|
25
|
-
headers: { 'Content-Type': 'application/json' },
|
|
26
|
-
body: JSON.stringify({
|
|
27
|
-
ticker: params.ticker,
|
|
28
|
-
fiscal_quarter: params.fiscalQuarter,
|
|
29
|
-
estimate_type: params.estimateType,
|
|
30
|
-
}),
|
|
31
|
-
signal: AbortSignal.timeout(10_000),
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (!res.ok) {
|
|
35
|
-
throw new Error(`xDEO API error: HTTP ${res.status}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return res.json();
|
|
39
|
-
}
|
|
40
|
-
}
|
package/src/lib/sml-api/xmit.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export interface XmitDecodeParams {
|
|
2
|
-
filingUrl: string;
|
|
3
|
-
parseTarget: 'executive_pay' | 'holdings' | 'ownership_changes' | 'all';
|
|
4
|
-
format: 'json' | 'markdown';
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export class XmitClient {
|
|
8
|
-
private static instance: XmitClient;
|
|
9
|
-
private readonly baseUrl: string;
|
|
10
|
-
|
|
11
|
-
private constructor() {
|
|
12
|
-
this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
static getInstance(): XmitClient {
|
|
16
|
-
if (!XmitClient.instance) {
|
|
17
|
-
XmitClient.instance = new XmitClient();
|
|
18
|
-
}
|
|
19
|
-
return XmitClient.instance;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async decode(params: XmitDecodeParams): Promise<unknown> {
|
|
23
|
-
const res = await fetch(`${this.baseUrl}/xmit/v1/decode`, {
|
|
24
|
-
method: 'POST',
|
|
25
|
-
headers: { 'Content-Type': 'application/json' },
|
|
26
|
-
body: JSON.stringify({
|
|
27
|
-
filing_url: params.filingUrl,
|
|
28
|
-
parse_target: params.parseTarget,
|
|
29
|
-
format: params.format,
|
|
30
|
-
}),
|
|
31
|
-
signal: AbortSignal.timeout(30_000),
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (!res.ok) {
|
|
35
|
-
throw new Error(`xMIT API error: HTTP ${res.status}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return res.json();
|
|
39
|
-
}
|
|
40
|
-
}
|
package/src/server/health.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import type { Request, Response } from 'express';
|
|
2
|
-
|
|
3
|
-
const startTime = Date.now();
|
|
4
|
-
|
|
5
|
-
export interface HealthStatus {
|
|
6
|
-
status: 'ok' | 'degraded';
|
|
7
|
-
version: string;
|
|
8
|
-
transport: string;
|
|
9
|
-
uptime_seconds: number;
|
|
10
|
-
uptime_human: string;
|
|
11
|
-
timestamp: string;
|
|
12
|
-
checks: {
|
|
13
|
-
process: 'ok';
|
|
14
|
-
memory_mb: number;
|
|
15
|
-
memory_ok: boolean;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function formatUptime(ms: number): string {
|
|
20
|
-
const s = Math.floor(ms / 1000);
|
|
21
|
-
const m = Math.floor(s / 60);
|
|
22
|
-
const h = Math.floor(m / 60);
|
|
23
|
-
const d = Math.floor(h / 24);
|
|
24
|
-
if (d > 0) return `${d}d ${h % 24}h ${m % 60}m`;
|
|
25
|
-
if (h > 0) return `${h}h ${m % 60}m ${s % 60}s`;
|
|
26
|
-
if (m > 0) return `${m}m ${s % 60}s`;
|
|
27
|
-
return `${s}s`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function healthHandler(_req: Request, res: Response): void {
|
|
31
|
-
const uptimeMs = Date.now() - startTime;
|
|
32
|
-
const memMb = Math.round(process.memoryUsage().rss / 1024 / 1024);
|
|
33
|
-
const memOk = memMb < 450; // warn if approaching 512MB container limit
|
|
34
|
-
|
|
35
|
-
const body: HealthStatus = {
|
|
36
|
-
status: memOk ? 'ok' : 'degraded',
|
|
37
|
-
version: process.env['npm_package_version'] ?? '1.0.0',
|
|
38
|
-
transport: process.env['MCP_TRANSPORT'] ?? 'stdio',
|
|
39
|
-
uptime_seconds: Math.floor(uptimeMs / 1000),
|
|
40
|
-
uptime_human: formatUptime(uptimeMs),
|
|
41
|
-
timestamp: new Date().toISOString(),
|
|
42
|
-
checks: {
|
|
43
|
-
process: 'ok',
|
|
44
|
-
memory_mb: memMb,
|
|
45
|
-
memory_ok: memOk,
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Return 200 even if degraded — let the orchestrator decide.
|
|
50
|
-
// Only return 5xx if the process itself is fundamentally broken.
|
|
51
|
-
res.status(200).json(body);
|
|
52
|
-
}
|
package/src/server/index.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
5
|
-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
6
|
-
import express from 'express';
|
|
7
|
-
import { randomUUID } from 'crypto';
|
|
8
|
-
import cors from 'cors';
|
|
9
|
-
import { registerTools } from './tools/index.js';
|
|
10
|
-
import { AuditLogger } from './security/audit.js';
|
|
11
|
-
import { RateLimiter } from './security/rate-limit.js';
|
|
12
|
-
import { healthHandler } from './health.js';
|
|
13
|
-
|
|
14
|
-
const VERSION = '1.0.0';
|
|
15
|
-
|
|
16
|
-
async function createServer(): Promise<McpServer> {
|
|
17
|
-
const server = new McpServer(
|
|
18
|
-
{ name: 'mcp-x402', version: VERSION },
|
|
19
|
-
{ capabilities: { tools: {} } },
|
|
20
|
-
);
|
|
21
|
-
await registerTools(server);
|
|
22
|
-
return server;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async function runStdio(): Promise<void> {
|
|
26
|
-
const server = await createServer();
|
|
27
|
-
const transport = new StdioServerTransport();
|
|
28
|
-
await server.connect(transport);
|
|
29
|
-
AuditLogger.getInstance().info('server_start', { transport: 'stdio', version: VERSION });
|
|
30
|
-
|
|
31
|
-
const shutdown = async () => {
|
|
32
|
-
AuditLogger.getInstance().info('server_stop', { transport: 'stdio' });
|
|
33
|
-
await server.close();
|
|
34
|
-
process.exit(0);
|
|
35
|
-
};
|
|
36
|
-
process.on('SIGINT', shutdown);
|
|
37
|
-
process.on('SIGTERM', shutdown);
|
|
38
|
-
|
|
39
|
-
// Keep stdio process alive
|
|
40
|
-
process.stdin.on('end', () => {
|
|
41
|
-
AuditLogger.getInstance().warn('stdio_stdin_end', {});
|
|
42
|
-
process.exit(0);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function runSSE(): Promise<void> {
|
|
47
|
-
const app = express();
|
|
48
|
-
const port = parseInt(process.env['MCP_SSE_PORT'] ?? '3402', 10);
|
|
49
|
-
|
|
50
|
-
app.use(cors({ origin: process.env['CORS_ORIGIN'] ?? '*' }));
|
|
51
|
-
app.use(express.json({ limit: '1mb' }));
|
|
52
|
-
|
|
53
|
-
// Health endpoint
|
|
54
|
-
app.get('/health', healthHandler);
|
|
55
|
-
|
|
56
|
-
// Wallet info — shows the server's derived wallet address (safe to expose, no private key)
|
|
57
|
-
app.get('/wallet', async (_req, res) => {
|
|
58
|
-
const { WalletManager } = await import('./payments/wallet.js');
|
|
59
|
-
const wallet = await WalletManager.getInstance().getOrCreateWallet();
|
|
60
|
-
res.json({ address: wallet.address, chain: wallet.chain, note: 'Fund this address with USDC on Base to enable outbound payments.' });
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
app.get('/agents.json', (_req, res) => {
|
|
64
|
-
res.sendFile('agents.json', { root: process.cwd() });
|
|
65
|
-
});
|
|
66
|
-
app.get('/llms.txt', (_req, res) => {
|
|
67
|
-
res.sendFile('llms.txt', { root: process.cwd() });
|
|
68
|
-
});
|
|
69
|
-
app.get('/.well-known/agentcard.json', (_req, res) => {
|
|
70
|
-
res.sendFile('.well-known/agentcard.json', { root: process.cwd() });
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// FIX: Root handler — was 404, now returns service discovery
|
|
74
|
-
app.get('/', (_req, res) => {
|
|
75
|
-
res.json({
|
|
76
|
-
name: 'mcp-x402',
|
|
77
|
-
version: VERSION,
|
|
78
|
-
description: 'The x402 Amazon — 43+ tools, pay-per-call via XRPL. scriptmasterlabs.com',
|
|
79
|
-
status: 'online',
|
|
80
|
-
transport: 'streamable-http + sse',
|
|
81
|
-
endpoints: {
|
|
82
|
-
mcp_streamable: 'POST /mcp',
|
|
83
|
-
sse_connect: 'GET /sse',
|
|
84
|
-
sse_messages: 'POST /messages',
|
|
85
|
-
health: 'GET /health',
|
|
86
|
-
agentCard: 'GET /.well-known/agentcard.json',
|
|
87
|
-
llms: 'GET /llms.txt',
|
|
88
|
-
},
|
|
89
|
-
links: {
|
|
90
|
-
github: 'https://github.com/Timwal78/SML_Portfolio/tree/main/mcp-x402',
|
|
91
|
-
homepage: 'https://scriptmasterlabs.com',
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Streamable HTTP transport — claude.ai web connectors
|
|
97
|
-
const streamableTransports = new Map<string, StreamableHTTPServerTransport>();
|
|
98
|
-
|
|
99
|
-
app.post('/mcp', async (req, res) => {
|
|
100
|
-
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
|
101
|
-
let transport = sessionId ? streamableTransports.get(sessionId) : undefined;
|
|
102
|
-
|
|
103
|
-
if (!transport) {
|
|
104
|
-
const newSessionId = randomUUID();
|
|
105
|
-
transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => newSessionId });
|
|
106
|
-
streamableTransports.set(newSessionId, transport);
|
|
107
|
-
transport.onclose = () => streamableTransports.delete(newSessionId);
|
|
108
|
-
const server = await createServer();
|
|
109
|
-
await server.connect(transport);
|
|
110
|
-
AuditLogger.getInstance().info('mcp_connect', { sessionId: newSessionId });
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
await transport.handleRequest(req, res, req.body);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// FIX: GET /mcp with no session was 404, now returns service info
|
|
117
|
-
app.get('/mcp', async (req, res) => {
|
|
118
|
-
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
|
119
|
-
const transport = sessionId ? streamableTransports.get(sessionId) : undefined;
|
|
120
|
-
if (!transport) {
|
|
121
|
-
res.json({
|
|
122
|
-
name: 'mcp-x402',
|
|
123
|
-
version: VERSION,
|
|
124
|
-
protocol: 'MCP/streamable-http',
|
|
125
|
-
status: 'ready',
|
|
126
|
-
tools: '43+ tools available',
|
|
127
|
-
how_to_connect: 'POST /mcp with a JSON-RPC initialize request',
|
|
128
|
-
sse_alternative: 'GET /sse for legacy SSE transport',
|
|
129
|
-
health: '/health',
|
|
130
|
-
homepage: 'https://scriptmasterlabs.com',
|
|
131
|
-
});
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
await transport.handleRequest(req, res);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
app.delete('/mcp', async (req, res) => {
|
|
138
|
-
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
|
139
|
-
const transport = sessionId ? streamableTransports.get(sessionId) : undefined;
|
|
140
|
-
if (!transport) { res.status(404).json({ error: 'session_not_found' }); return; }
|
|
141
|
-
await transport.handleRequest(req, res);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const transports = new Map<string, SSEServerTransport>();
|
|
145
|
-
const rateLimiter = RateLimiter.getInstance();
|
|
146
|
-
|
|
147
|
-
app.get('/sse', async (req, res) => {
|
|
148
|
-
const clientIp = req.ip ?? 'unknown';
|
|
149
|
-
if (!rateLimiter.checkIp(clientIp)) {
|
|
150
|
-
res.status(429).json({ error: 'rate_limit_exceeded', retry_after: 60 });
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const transport = new SSEServerTransport('/messages', res);
|
|
154
|
-
const sessionId = transport.sessionId;
|
|
155
|
-
transports.set(sessionId, transport);
|
|
156
|
-
const server = await createServer();
|
|
157
|
-
await server.connect(transport);
|
|
158
|
-
AuditLogger.getInstance().info('sse_connect', { sessionId, clientIp });
|
|
159
|
-
res.on('close', async () => {
|
|
160
|
-
transports.delete(sessionId);
|
|
161
|
-
AuditLogger.getInstance().info('sse_disconnect', { sessionId });
|
|
162
|
-
await server.close();
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
app.post('/messages', async (req, res) => {
|
|
167
|
-
const sessionId = req.query['sessionId'] as string | undefined;
|
|
168
|
-
if (!sessionId) { res.status(400).json({ error: 'missing_session_id' }); return; }
|
|
169
|
-
const transport = transports.get(sessionId);
|
|
170
|
-
if (!transport) { res.status(404).json({ error: 'session_not_found' }); return; }
|
|
171
|
-
await transport.handlePostMessage(req, res);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
const httpServer = await new Promise<ReturnType<typeof app.listen>>(
|
|
175
|
-
(resolve) => {
|
|
176
|
-
const s = app.listen(port, () => resolve(s));
|
|
177
|
-
},
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
AuditLogger.getInstance().info('server_start', { transport: 'sse', port, version: VERSION });
|
|
181
|
-
console.error(`[mcp-x402] listening on :${port} — health: http://localhost:${port}/health`);
|
|
182
|
-
|
|
183
|
-
const shutdown = async () => {
|
|
184
|
-
AuditLogger.getInstance().info('server_stop', { transport: 'sse' });
|
|
185
|
-
for (const [id] of transports) {
|
|
186
|
-
AuditLogger.getInstance().info('sse_force_close', { sessionId: id });
|
|
187
|
-
}
|
|
188
|
-
httpServer.close(() => process.exit(0));
|
|
189
|
-
setTimeout(() => process.exit(1), 10_000).unref();
|
|
190
|
-
};
|
|
191
|
-
process.on('SIGINT', shutdown);
|
|
192
|
-
process.on('SIGTERM', shutdown);
|
|
193
|
-
|
|
194
|
-
process.on('uncaughtException', (err) => {
|
|
195
|
-
AuditLogger.getInstance().error('uncaught_exception', { error: String(err), stack: err.stack ?? '' });
|
|
196
|
-
});
|
|
197
|
-
process.on('unhandledRejection', (reason) => {
|
|
198
|
-
AuditLogger.getInstance().error('unhandledRejection', { reason: String(reason) });
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const transport = process.env['MCP_TRANSPORT'] ?? 'stdio';
|
|
203
|
-
if (transport === 'sse') {
|
|
204
|
-
runSSE().catch((err) => {
|
|
205
|
-
console.error('[mcp-x402] fatal:', err);
|
|
206
|
-
process.exit(1);
|
|
207
|
-
});
|
|
208
|
-
} else {
|
|
209
|
-
runStdio().catch((err) => {
|
|
210
|
-
console.error('[mcp-x402] fatal:', err);
|
|
211
|
-
process.exit(1);
|
|
212
|
-
});
|
|
213
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { AuditLogger } from '../security/audit.js';
|
|
2
|
-
|
|
3
|
-
export interface MandateParams {
|
|
4
|
-
maxAmount: string;
|
|
5
|
-
currency: string;
|
|
6
|
-
toolName: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface MandateCache {
|
|
10
|
-
valid: boolean;
|
|
11
|
-
expiresAt: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// In-memory mandate cache — 5 minute TTL per wallet+tool combination
|
|
15
|
-
const mandateCache = new Map<string, MandateCache>();
|
|
16
|
-
|
|
17
|
-
export class AP2Client {
|
|
18
|
-
private static instance: AP2Client;
|
|
19
|
-
private readonly baseUrl: string;
|
|
20
|
-
|
|
21
|
-
private constructor() {
|
|
22
|
-
this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static getInstance(): AP2Client {
|
|
26
|
-
if (!AP2Client.instance) {
|
|
27
|
-
AP2Client.instance = new AP2Client();
|
|
28
|
-
}
|
|
29
|
-
return AP2Client.instance;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async verifyMandate(wallet: string, params: MandateParams): Promise<boolean> {
|
|
33
|
-
const cacheKey = `${wallet}:${params.toolName}:${params.currency}`;
|
|
34
|
-
const cached = mandateCache.get(cacheKey);
|
|
35
|
-
|
|
36
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
37
|
-
return cached.valid;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const audit = AuditLogger.getInstance();
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const controller = new AbortController();
|
|
44
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
45
|
-
|
|
46
|
-
const res = await fetch(`${this.baseUrl}/ap2/v1/mandate/verify`, {
|
|
47
|
-
method: 'POST',
|
|
48
|
-
headers: { 'Content-Type': 'application/json' },
|
|
49
|
-
body: JSON.stringify({
|
|
50
|
-
wallet,
|
|
51
|
-
max_amount: params.maxAmount,
|
|
52
|
-
currency: params.currency,
|
|
53
|
-
tool: params.toolName,
|
|
54
|
-
}),
|
|
55
|
-
signal: controller.signal,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
clearTimeout(timeout);
|
|
59
|
-
|
|
60
|
-
if (!res.ok) {
|
|
61
|
-
audit.warn('ap2_mandate_http_error', { status: res.status, wallet });
|
|
62
|
-
mandateCache.set(cacheKey, { valid: false, expiresAt: Date.now() + 60_000 });
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const body = (await res.json()) as { valid: boolean; expires_in?: number };
|
|
67
|
-
const ttl = (body.expires_in ?? 300) * 1000;
|
|
68
|
-
|
|
69
|
-
mandateCache.set(cacheKey, { valid: body.valid, expiresAt: Date.now() + ttl });
|
|
70
|
-
return body.valid;
|
|
71
|
-
} catch (err) {
|
|
72
|
-
audit.error('ap2_mandate_error', { error: String(err), wallet });
|
|
73
|
-
// Fail open when AP2 service is unreachable — log and allow
|
|
74
|
-
audit.warn('ap2_mandate_fallback', { wallet, tool: params.toolName, note: 'AP2 unreachable, auto-approving' });
|
|
75
|
-
mandateCache.set(cacheKey, { valid: true, expiresAt: Date.now() + 60_000 });
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async createMandate(
|
|
81
|
-
wallet: string,
|
|
82
|
-
params: { dailyCap: string; currency: string },
|
|
83
|
-
): Promise<string> {
|
|
84
|
-
const res = await fetch(`${this.baseUrl}/ap2/v1/mandate/create`, {
|
|
85
|
-
method: 'POST',
|
|
86
|
-
headers: { 'Content-Type': 'application/json' },
|
|
87
|
-
body: JSON.stringify({
|
|
88
|
-
wallet,
|
|
89
|
-
daily_cap: params.dailyCap,
|
|
90
|
-
currency: params.currency,
|
|
91
|
-
}),
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (!res.ok) {
|
|
95
|
-
throw new Error(`AP2 mandate creation failed: HTTP ${res.status}`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const body = (await res.json()) as { mandate_id: string };
|
|
99
|
-
return body.mandate_id;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'crypto';
|
|
2
|
-
import { AuditLogger } from '../security/audit.js';
|
|
3
|
-
|
|
4
|
-
export interface ReceiptInput {
|
|
5
|
-
txHash: string;
|
|
6
|
-
chain: string;
|
|
7
|
-
amount: string;
|
|
8
|
-
currency: string;
|
|
9
|
-
tool: string;
|
|
10
|
-
wallet: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface Receipt {
|
|
14
|
-
id: string;
|
|
15
|
-
txHash: string;
|
|
16
|
-
chain: string;
|
|
17
|
-
amount: string;
|
|
18
|
-
currency: string;
|
|
19
|
-
tool: string;
|
|
20
|
-
wallet: string;
|
|
21
|
-
issuedAt: number;
|
|
22
|
-
hash: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class ReceiptStore {
|
|
26
|
-
private static instance: ReceiptStore;
|
|
27
|
-
private readonly baseUrl: string;
|
|
28
|
-
|
|
29
|
-
private constructor() {
|
|
30
|
-
this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static getInstance(): ReceiptStore {
|
|
34
|
-
if (!ReceiptStore.instance) {
|
|
35
|
-
ReceiptStore.instance = new ReceiptStore();
|
|
36
|
-
}
|
|
37
|
-
return ReceiptStore.instance;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async create(input: ReceiptInput): Promise<Receipt> {
|
|
41
|
-
const id = randomUUID();
|
|
42
|
-
const issuedAt = Date.now();
|
|
43
|
-
|
|
44
|
-
// Content hash for tamper detection
|
|
45
|
-
const hash = createHash('sha256')
|
|
46
|
-
.update(`${id}:${input.txHash}:${input.chain}:${input.amount}:${input.currency}:${input.tool}:${input.wallet}:${issuedAt}`)
|
|
47
|
-
.digest('hex');
|
|
48
|
-
|
|
49
|
-
const receipt: Receipt = { id, ...input, issuedAt, hash };
|
|
50
|
-
|
|
51
|
-
// Attempt to register with 402Proof server (N7)
|
|
52
|
-
try {
|
|
53
|
-
await this.registerWithProofServer(receipt);
|
|
54
|
-
} catch (err) {
|
|
55
|
-
// Log but don't fail — local receipt is still valid
|
|
56
|
-
AuditLogger.getInstance().warn('proof_server_register_fail', { error: String(err), receiptId: id });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
AuditLogger.getInstance().info('receipt_issued', { receiptId: id, tool: input.tool });
|
|
60
|
-
return receipt;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private async registerWithProofServer(receipt: Receipt): Promise<void> {
|
|
64
|
-
const proofUrl = process.env['PROOF402_URL'] ?? 'https://four02proof.onrender.com';
|
|
65
|
-
const res = await fetch(`${proofUrl}/v1/receipt`, {
|
|
66
|
-
method: 'POST',
|
|
67
|
-
headers: { 'Content-Type': 'application/json' },
|
|
68
|
-
body: JSON.stringify({
|
|
69
|
-
receipt_id: receipt.id,
|
|
70
|
-
tx_hash: receipt.txHash,
|
|
71
|
-
chain: receipt.chain,
|
|
72
|
-
amount: receipt.amount,
|
|
73
|
-
currency: receipt.currency,
|
|
74
|
-
tool: receipt.tool,
|
|
75
|
-
wallet: receipt.wallet,
|
|
76
|
-
issued_at: receipt.issuedAt,
|
|
77
|
-
hash: receipt.hash,
|
|
78
|
-
}),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
if (!res.ok) {
|
|
82
|
-
throw new Error(`402Proof server returned HTTP ${res.status}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|