@xona-labs/xpay 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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/dist/cli/accounts.d.ts +13 -0
  4. package/dist/cli/accounts.d.ts.map +1 -0
  5. package/dist/cli/accounts.js +66 -0
  6. package/dist/cli/accounts.js.map +1 -0
  7. package/dist/cli/balance.d.ts +14 -0
  8. package/dist/cli/balance.d.ts.map +1 -0
  9. package/dist/cli/balance.js +60 -0
  10. package/dist/cli/balance.js.map +1 -0
  11. package/dist/cli/common.d.ts +20 -0
  12. package/dist/cli/common.d.ts.map +1 -0
  13. package/dist/cli/common.js +58 -0
  14. package/dist/cli/common.js.map +1 -0
  15. package/dist/cli/discover.d.ts +12 -0
  16. package/dist/cli/discover.d.ts.map +1 -0
  17. package/dist/cli/discover.js +69 -0
  18. package/dist/cli/discover.js.map +1 -0
  19. package/dist/cli/guardrail.d.ts +16 -0
  20. package/dist/cli/guardrail.d.ts.map +1 -0
  21. package/dist/cli/guardrail.js +71 -0
  22. package/dist/cli/guardrail.js.map +1 -0
  23. package/dist/cli/history.d.ts +16 -0
  24. package/dist/cli/history.d.ts.map +1 -0
  25. package/dist/cli/history.js +59 -0
  26. package/dist/cli/history.js.map +1 -0
  27. package/dist/cli/index.d.ts +17 -0
  28. package/dist/cli/index.d.ts.map +1 -0
  29. package/dist/cli/index.js +165 -0
  30. package/dist/cli/index.js.map +1 -0
  31. package/dist/cli/init.d.ts +23 -0
  32. package/dist/cli/init.d.ts.map +1 -0
  33. package/dist/cli/init.js +114 -0
  34. package/dist/cli/init.js.map +1 -0
  35. package/dist/cli/mcp-server.d.ts +26 -0
  36. package/dist/cli/mcp-server.d.ts.map +1 -0
  37. package/dist/cli/mcp-server.js +112 -0
  38. package/dist/cli/mcp-server.js.map +1 -0
  39. package/dist/cli/pay.d.ts +15 -0
  40. package/dist/cli/pay.d.ts.map +1 -0
  41. package/dist/cli/pay.js +76 -0
  42. package/dist/cli/pay.js.map +1 -0
  43. package/dist/cli/transfer.d.ts +19 -0
  44. package/dist/cli/transfer.d.ts.map +1 -0
  45. package/dist/cli/transfer.js +66 -0
  46. package/dist/cli/transfer.js.map +1 -0
  47. package/dist/discover/cache.d.ts +24 -0
  48. package/dist/discover/cache.d.ts.map +1 -0
  49. package/dist/discover/cache.js +94 -0
  50. package/dist/discover/cache.js.map +1 -0
  51. package/dist/discover/index.d.ts +21 -0
  52. package/dist/discover/index.d.ts.map +1 -0
  53. package/dist/discover/index.js +88 -0
  54. package/dist/discover/index.js.map +1 -0
  55. package/dist/discover/payai.d.ts +18 -0
  56. package/dist/discover/payai.d.ts.map +1 -0
  57. package/dist/discover/payai.js +56 -0
  58. package/dist/discover/payai.js.map +1 -0
  59. package/dist/do/index.d.ts +17 -0
  60. package/dist/do/index.d.ts.map +1 -0
  61. package/dist/do/index.js +32 -0
  62. package/dist/do/index.js.map +1 -0
  63. package/dist/guardrail/index.d.ts +47 -0
  64. package/dist/guardrail/index.d.ts.map +1 -0
  65. package/dist/guardrail/index.js +99 -0
  66. package/dist/guardrail/index.js.map +1 -0
  67. package/dist/history/evm.d.ts +21 -0
  68. package/dist/history/evm.d.ts.map +1 -0
  69. package/dist/history/evm.js +118 -0
  70. package/dist/history/evm.js.map +1 -0
  71. package/dist/history/index.d.ts +19 -0
  72. package/dist/history/index.d.ts.map +1 -0
  73. package/dist/history/index.js +45 -0
  74. package/dist/history/index.js.map +1 -0
  75. package/dist/history/solana.d.ts +20 -0
  76. package/dist/history/solana.d.ts.map +1 -0
  77. package/dist/history/solana.js +97 -0
  78. package/dist/history/solana.js.map +1 -0
  79. package/dist/history/types.d.ts +26 -0
  80. package/dist/history/types.d.ts.map +1 -0
  81. package/dist/history/types.js +10 -0
  82. package/dist/history/types.js.map +1 -0
  83. package/dist/index.d.ts +94 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +74 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/profile/derive.d.ts +28 -0
  88. package/dist/profile/derive.d.ts.map +1 -0
  89. package/dist/profile/derive.js +75 -0
  90. package/dist/profile/derive.js.map +1 -0
  91. package/dist/profile/index.d.ts +74 -0
  92. package/dist/profile/index.d.ts.map +1 -0
  93. package/dist/profile/index.js +115 -0
  94. package/dist/profile/index.js.map +1 -0
  95. package/dist/profile/storage.d.ts +35 -0
  96. package/dist/profile/storage.d.ts.map +1 -0
  97. package/dist/profile/storage.js +162 -0
  98. package/dist/profile/storage.js.map +1 -0
  99. package/dist/profile/types.d.ts +69 -0
  100. package/dist/profile/types.d.ts.map +1 -0
  101. package/dist/profile/types.js +9 -0
  102. package/dist/profile/types.js.map +1 -0
  103. package/dist/signers/index.d.ts +5 -0
  104. package/dist/signers/index.d.ts.map +1 -0
  105. package/dist/signers/index.js +5 -0
  106. package/dist/signers/index.js.map +1 -0
  107. package/dist/signers/phantom.d.ts +14 -0
  108. package/dist/signers/phantom.d.ts.map +1 -0
  109. package/dist/signers/phantom.js +12 -0
  110. package/dist/signers/phantom.js.map +1 -0
  111. package/dist/signers/privy.d.ts +20 -0
  112. package/dist/signers/privy.d.ts.map +1 -0
  113. package/dist/signers/privy.js +15 -0
  114. package/dist/signers/privy.js.map +1 -0
  115. package/dist/signers/raw-evm.d.ts +15 -0
  116. package/dist/signers/raw-evm.d.ts.map +1 -0
  117. package/dist/signers/raw-evm.js +70 -0
  118. package/dist/signers/raw-evm.js.map +1 -0
  119. package/dist/signers/raw-solana.d.ts +15 -0
  120. package/dist/signers/raw-solana.d.ts.map +1 -0
  121. package/dist/signers/raw-solana.js +88 -0
  122. package/dist/signers/raw-solana.js.map +1 -0
  123. package/dist/tools/index.d.ts +48 -0
  124. package/dist/tools/index.d.ts.map +1 -0
  125. package/dist/tools/index.js +164 -0
  126. package/dist/tools/index.js.map +1 -0
  127. package/dist/transfer/index.d.ts +30 -0
  128. package/dist/transfer/index.d.ts.map +1 -0
  129. package/dist/transfer/index.js +86 -0
  130. package/dist/transfer/index.js.map +1 -0
  131. package/dist/types.d.ts +203 -0
  132. package/dist/types.d.ts.map +1 -0
  133. package/dist/types.js +56 -0
  134. package/dist/types.js.map +1 -0
  135. package/dist/use/index.d.ts +38 -0
  136. package/dist/use/index.d.ts.map +1 -0
  137. package/dist/use/index.js +173 -0
  138. package/dist/use/index.js.map +1 -0
  139. package/dist/wallet/index.d.ts +31 -0
  140. package/dist/wallet/index.d.ts.map +1 -0
  141. package/dist/wallet/index.js +56 -0
  142. package/dist/wallet/index.js.map +1 -0
  143. package/package.json +70 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * xPay MCP server.
3
+ *
4
+ * Exposes every CLI capability as an MCP tool over stdio. Designed to be
5
+ * dropped into Claude Desktop / Cursor / Codex configs verbatim:
6
+ *
7
+ * {
8
+ * "mcpServers": {
9
+ * "xpay": {
10
+ * "command": "npx",
11
+ * "args": ["-y", "@xona-labs/xpay"],
12
+ * "env": { "XPAY_PASSPHRASE": "<your-passphrase>" }
13
+ * }
14
+ * }
15
+ * }
16
+ *
17
+ * Configuration via env:
18
+ * XPAY_PROFILE profile to load (default: active profile, else "default")
19
+ * XPAY_PASSPHRASE passphrase for encrypted wallets
20
+ * XPAY_HOME override ~/.xpay/ root (mirrors the CLI)
21
+ *
22
+ * For ephemeral / test setups you can still bypass profiles with:
23
+ * XPAY_SOLANA_SECRET, XPAY_EVM_KEY, XPAY_EVM_NETWORK
24
+ */
25
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
26
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
27
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
28
+ import { createXPay, loadProfile } from "../index.js";
29
+ import { profileExists } from "../profile/storage.js";
30
+ import { rawSolanaSigner } from "../signers/raw-solana.js";
31
+ import { rawEvmSigner } from "../signers/raw-evm.js";
32
+ import { forClaude } from "../tools/index.js";
33
+ import { getActiveProfile } from "./accounts.js";
34
+ export async function startMcpServer() {
35
+ const xpay = await buildXPay();
36
+ const { tools, handlers } = forClaude(xpay);
37
+ const server = new Server({ name: "xpay", version: "0.1.0" }, { capabilities: { tools: {} } });
38
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
39
+ tools: tools.map((t) => ({
40
+ name: t.name,
41
+ description: t.description,
42
+ inputSchema: t.input_schema,
43
+ })),
44
+ }));
45
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
46
+ const handler = handlers[req.params.name];
47
+ if (!handler) {
48
+ throw new Error(`Unknown tool: ${req.params.name}`);
49
+ }
50
+ try {
51
+ const result = await handler(req.params.arguments ?? {});
52
+ return {
53
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
54
+ };
55
+ }
56
+ catch (err) {
57
+ const message = err instanceof Error ? err.message : String(err);
58
+ return {
59
+ content: [{ type: "text", text: `Error: ${message}` }],
60
+ isError: true,
61
+ };
62
+ }
63
+ });
64
+ const transport = new StdioServerTransport();
65
+ await server.connect(transport);
66
+ }
67
+ /**
68
+ * Build the XPay client the MCP host will use. Preference order:
69
+ * 1. A loaded profile (the common case after `xpay init`).
70
+ * 2. Raw signer envs (for tests / ephemeral pods that skip the profile flow).
71
+ */
72
+ async function buildXPay() {
73
+ const profileName = process.env.XPAY_PROFILE ?? getActiveProfile();
74
+ if (profileExists(profileName)) {
75
+ const profile = await loadProfile({
76
+ name: profileName,
77
+ passphrase: process.env.XPAY_PASSPHRASE,
78
+ });
79
+ return createXPay({ profile });
80
+ }
81
+ // Fallback: ephemeral raw signers.
82
+ const networks = (process.env.XPAY_NETWORKS ?? "solana,base").split(",");
83
+ const signers = {};
84
+ if (process.env.XPAY_SOLANA_SECRET) {
85
+ signers["solana"] = rawSolanaSigner({ secretKey: process.env.XPAY_SOLANA_SECRET });
86
+ }
87
+ if (process.env.XPAY_EVM_KEY) {
88
+ const evmNet = (process.env.XPAY_EVM_NETWORK ?? "base");
89
+ signers[evmNet] = rawEvmSigner({ privateKey: process.env.XPAY_EVM_KEY, network: evmNet });
90
+ }
91
+ if (Object.keys(signers).length === 0) {
92
+ throw new Error(`xpay-mcp: no profile "${profileName}" found and no raw signer env set. ` +
93
+ `Run \`xpay init\` first, or set XPAY_SOLANA_SECRET / XPAY_EVM_KEY.`);
94
+ }
95
+ return createXPay({
96
+ networks,
97
+ signers,
98
+ guardrail: {
99
+ maxPerTx: numericEnv("XPAY_MAX_PER_TX"),
100
+ maxPerDay: numericEnv("XPAY_MAX_PER_DAY"),
101
+ allowedHosts: process.env.XPAY_ALLOWED_HOSTS?.split(","),
102
+ },
103
+ });
104
+ }
105
+ function numericEnv(name) {
106
+ const v = process.env[name];
107
+ if (v === undefined || v === "")
108
+ return undefined;
109
+ const n = Number(v);
110
+ return Number.isFinite(n) ? n : undefined;
111
+ }
112
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../src/cli/mcp-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAIjD,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC;IAC/B,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAClC,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,YAAY;SAC5B,CAAC,CAAC;KACJ,CAAC,CAAC,CAAC;IAEJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5D,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;YACzD,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;gBACtD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,SAAS;IACtB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,gBAAgB,EAAE,CAAC;IACnE,IAAI,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC;YAChC,IAAI,EAAE,WAAW;YACjB,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;SACxC,CAAC,CAAC;QACH,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,mCAAmC;IACnC,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,aAAa,CAAC,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;IACtF,MAAM,OAAO,GAAqC,EAAE,CAAC;IACrD,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC;QACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,eAAe,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,MAAM,CAAY,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,yBAAyB,WAAW,qCAAqC;YACvE,oEAAoE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;QAChB,QAAQ;QACR,OAAO;QACP,SAAS,EAAE;YACT,QAAQ,EAAE,UAAU,CAAC,iBAAiB,CAAC;YACvC,SAAS,EAAE,UAAU,CAAC,kBAAkB,CAAC;YACzC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,CAAC,GAAG,CAAC;SACzD;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAClD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `xpay pay <url>` — pay an x402 endpoint.
3
+ *
4
+ * Accepts either a catalog URL (we resolve metadata via discover) or any
5
+ * URL that returns HTTP 402 (live challenge mode).
6
+ */
7
+ export interface PayCmdOptions {
8
+ profile?: string;
9
+ passphrase?: string;
10
+ maxUsd?: string;
11
+ body?: string;
12
+ yes?: boolean;
13
+ }
14
+ export declare function runPay(url: string, opts: PayCmdOptions): Promise<void>;
15
+ //# sourceMappingURL=pay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pay.d.ts","sourceRoot":"","sources":["../../src/cli/pay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAuE5E"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * `xpay pay <url>` — pay an x402 endpoint.
3
+ *
4
+ * Accepts either a catalog URL (we resolve metadata via discover) or any
5
+ * URL that returns HTTP 402 (live challenge mode).
6
+ */
7
+ import chalk from "chalk";
8
+ import inquirer from "inquirer";
9
+ import { createXPay } from "../index.js";
10
+ import { unlockActive, formatUsd } from "./common.js";
11
+ export async function runPay(url, opts) {
12
+ if (!url) {
13
+ console.error(chalk.red("✗ usage: xpay pay <url> [--max-usd N] [--body '{...}']"));
14
+ process.exit(1);
15
+ }
16
+ const profile = await unlockActive(opts);
17
+ // Optional guardrail override from the command line.
18
+ if (opts.maxUsd) {
19
+ profile.config.guardrail = {
20
+ ...profile.config.guardrail,
21
+ maxPerTx: Number(opts.maxUsd),
22
+ };
23
+ }
24
+ const xpay = createXPay({ profile });
25
+ // Pre-flight: parse the body if supplied.
26
+ let body;
27
+ if (opts.body) {
28
+ try {
29
+ body = JSON.parse(opts.body);
30
+ }
31
+ catch {
32
+ console.error(chalk.red("✗ --body must be valid JSON"));
33
+ process.exit(1);
34
+ }
35
+ }
36
+ // Confirm spend if interactive and no --yes.
37
+ if (process.stdin.isTTY && !opts.yes) {
38
+ const { go } = await inquirer.prompt([
39
+ {
40
+ type: "confirm",
41
+ name: "go",
42
+ message: `Pay and call ${chalk.cyan(url)}? (caps: ${profile.config.guardrail?.maxPerTx
43
+ ? `$${profile.config.guardrail.maxPerTx}/tx`
44
+ : "none"})`,
45
+ default: true,
46
+ },
47
+ ]);
48
+ if (!go) {
49
+ console.log(chalk.yellow("Cancelled."));
50
+ return;
51
+ }
52
+ }
53
+ const t0 = Date.now();
54
+ try {
55
+ const result = await xpay.useByUrl(url, { body });
56
+ const elapsed = Date.now() - t0;
57
+ const amount = Number(result.amountPaid) / 1_000_000;
58
+ console.log("");
59
+ console.log(chalk.green(`✔ Paid ${formatUsd(amount)} on ${chalk.cyan(result.network)} in ${elapsed}ms`));
60
+ if (result.txSig) {
61
+ console.log(` ${chalk.dim("tx:")} ${result.txSig}`);
62
+ }
63
+ console.log("");
64
+ console.log(chalk.bold("Response:"));
65
+ const display = typeof result.data === "string" ? result.data : JSON.stringify(result.data, null, 2);
66
+ console.log(display.slice(0, 2000));
67
+ if (display.length > 2000)
68
+ console.log(chalk.dim(`\n ...truncated (${display.length} chars total)`));
69
+ }
70
+ catch (err) {
71
+ console.error("");
72
+ console.error(chalk.red(`✗ ${err.message}`));
73
+ process.exit(1);
74
+ }
75
+ }
76
+ //# sourceMappingURL=pay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pay.js","sourceRoot":"","sources":["../../src/cli/pay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `xpay transfer <amount> <token> <to>` — send USDC.
3
+ *
4
+ * Examples:
5
+ * xpay transfer 5 USDC 7G73PL...gC
6
+ * xpay transfer 1 USDC 0x47ff...5552 --network base
7
+ *
8
+ * Network is auto-detected from the address shape (0x → EVM, else Solana).
9
+ * If the wallet has multiple EVM signers, --network is required.
10
+ */
11
+ import type { Network } from "../types.js";
12
+ export interface TransferCmdOptions {
13
+ profile?: string;
14
+ passphrase?: string;
15
+ network?: Network;
16
+ yes?: boolean;
17
+ }
18
+ export declare function runTransfer(amountArg: string, tokenArg: string, to: string, opts: TransferCmdOptions): Promise<void>;
19
+ //# sourceMappingURL=transfer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../src/cli/transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAwDf"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `xpay transfer <amount> <token> <to>` — send USDC.
3
+ *
4
+ * Examples:
5
+ * xpay transfer 5 USDC 7G73PL...gC
6
+ * xpay transfer 1 USDC 0x47ff...5552 --network base
7
+ *
8
+ * Network is auto-detected from the address shape (0x → EVM, else Solana).
9
+ * If the wallet has multiple EVM signers, --network is required.
10
+ */
11
+ import chalk from "chalk";
12
+ import inquirer from "inquirer";
13
+ import { createXPay } from "../index.js";
14
+ import { unlockActive, formatUsd, shortAddress } from "./common.js";
15
+ export async function runTransfer(amountArg, tokenArg, to, opts) {
16
+ const amount = Number(amountArg);
17
+ if (!(amount > 0)) {
18
+ console.error(chalk.red(`✗ amount must be a positive number (got "${amountArg}")`));
19
+ process.exit(1);
20
+ }
21
+ const token = tokenArg.toUpperCase();
22
+ if (token !== "USDC") {
23
+ console.error(chalk.red(`✗ only USDC supported in v1 (got "${tokenArg}")`));
24
+ process.exit(1);
25
+ }
26
+ if (!to) {
27
+ console.error(chalk.red("✗ usage: xpay transfer <amount> USDC <to>"));
28
+ process.exit(1);
29
+ }
30
+ const profile = await unlockActive(opts);
31
+ const xpay = createXPay({ profile });
32
+ // Confirm.
33
+ if (process.stdin.isTTY && !opts.yes) {
34
+ const { go } = await inquirer.prompt([
35
+ {
36
+ type: "confirm",
37
+ name: "go",
38
+ message: `Send ${chalk.bold(`$${amount.toFixed(4)} USDC`)} to ${chalk.cyan(shortAddress(to, 6, 6))}${opts.network ? ` on ${chalk.cyan(opts.network)}` : ""}?`,
39
+ default: false,
40
+ },
41
+ ]);
42
+ if (!go) {
43
+ console.log(chalk.yellow("Cancelled."));
44
+ return;
45
+ }
46
+ }
47
+ try {
48
+ const t0 = Date.now();
49
+ const result = await xpay.transfer({
50
+ amount,
51
+ to,
52
+ network: opts.network,
53
+ token: "USDC",
54
+ });
55
+ const elapsed = Date.now() - t0;
56
+ console.log("");
57
+ console.log(chalk.green(`✔ Sent ${formatUsd(result.amount)} USDC on ${chalk.cyan(result.network)} in ${elapsed}ms`));
58
+ console.log(` ${chalk.dim("→ to:")} ${result.to}`);
59
+ console.log(` ${chalk.dim("→ tx:")} ${result.txSig}`);
60
+ }
61
+ catch (err) {
62
+ console.error(chalk.red(`\n✗ ${err.message}`));
63
+ process.exit(1);
64
+ }
65
+ }
66
+ //# sourceMappingURL=transfer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/cli/transfer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Module-level cache for catalog fetches.
3
+ *
4
+ * Discovery against PayAI's full 21k-item catalog takes ~50s on a cold fetch,
5
+ * which is unusable in a UI search loop. We cache the result in memory with a
6
+ * TTL so repeat lookups (and concurrent ones via the in-flight promise) hit
7
+ * the cache instead.
8
+ *
9
+ * Trade-off: a single process holds one snapshot per source. For server
10
+ * deployments (the platform's API route), this naturally warms after the
11
+ * first request. For very long-running processes, the TTL ensures freshness.
12
+ */
13
+ import type { Resource } from "../types.js";
14
+ type Loader = () => Promise<Resource[]>;
15
+ export declare function setCacheTtl(ms: number): void;
16
+ /**
17
+ * Get cached resources for `key`, loading via `loader` if missing or expired.
18
+ * Concurrent calls during a cold load share the same in-flight promise.
19
+ */
20
+ export declare function cached(key: string, loader: Loader): Promise<Resource[]>;
21
+ /** Force-evict a cache entry (or all entries with no arg). */
22
+ export declare function invalidate(key?: string): void;
23
+ export {};
24
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/discover/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;AAmDxC,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CA4B7E;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAG7C"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Module-level cache for catalog fetches.
3
+ *
4
+ * Discovery against PayAI's full 21k-item catalog takes ~50s on a cold fetch,
5
+ * which is unusable in a UI search loop. We cache the result in memory with a
6
+ * TTL so repeat lookups (and concurrent ones via the in-flight promise) hit
7
+ * the cache instead.
8
+ *
9
+ * Trade-off: a single process holds one snapshot per source. For server
10
+ * deployments (the platform's API route), this naturally warms after the
11
+ * first request. For very long-running processes, the TTL ensures freshness.
12
+ */
13
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { homedir } from "node:os";
16
+ const DEFAULT_TTL_MS = 10 * 60 * 1000; // 10 minutes
17
+ const store = new Map();
18
+ /** Where on-disk cache lives. Honors $XPAY_HOME (mirrors profile storage). */
19
+ function diskCacheDir() {
20
+ const root = process.env.XPAY_HOME ?? join(homedir(), ".xpay");
21
+ return join(root, "cache");
22
+ }
23
+ function diskCachePath(key) {
24
+ const safe = key.replace(/[^a-zA-Z0-9_-]+/g, "_");
25
+ return join(diskCacheDir(), `${safe}.json`);
26
+ }
27
+ function readDisk(key) {
28
+ try {
29
+ const file = diskCachePath(key);
30
+ if (!existsSync(file))
31
+ return null;
32
+ const blob = JSON.parse(readFileSync(file, "utf8"));
33
+ if (blob.expiresAt > Date.now())
34
+ return blob.data;
35
+ return null;
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ function writeDisk(key, data) {
42
+ try {
43
+ const dir = diskCacheDir();
44
+ if (!existsSync(dir))
45
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
46
+ const blob = { expiresAt: Date.now() + ttlMs, data };
47
+ writeFileSync(diskCachePath(key), JSON.stringify(blob));
48
+ }
49
+ catch {
50
+ // Best-effort — cache failures should never break the call.
51
+ }
52
+ }
53
+ /** TTL is configurable at runtime; defaults to 10 min. */
54
+ let ttlMs = DEFAULT_TTL_MS;
55
+ export function setCacheTtl(ms) {
56
+ ttlMs = ms;
57
+ }
58
+ /**
59
+ * Get cached resources for `key`, loading via `loader` if missing or expired.
60
+ * Concurrent calls during a cold load share the same in-flight promise.
61
+ */
62
+ export async function cached(key, loader) {
63
+ const now = Date.now();
64
+ const entry = store.get(key);
65
+ if (entry?.data && entry.expiresAt > now)
66
+ return entry.data;
67
+ if (entry?.inflight)
68
+ return entry.inflight;
69
+ // Try disk before paying the network cost. CLI invocations hit this path
70
+ // every time (each invocation is a fresh process with empty memory).
71
+ const onDisk = readDisk(key);
72
+ if (onDisk) {
73
+ store.set(key, { data: onDisk, expiresAt: now + ttlMs });
74
+ return onDisk;
75
+ }
76
+ const inflight = loader().then((data) => {
77
+ store.set(key, { data, expiresAt: Date.now() + ttlMs });
78
+ writeDisk(key, data);
79
+ return data;
80
+ }, (err) => {
81
+ store.delete(key);
82
+ throw err;
83
+ });
84
+ store.set(key, { expiresAt: 0, inflight });
85
+ return inflight;
86
+ }
87
+ /** Force-evict a cache entry (or all entries with no arg). */
88
+ export function invalidate(key) {
89
+ if (key)
90
+ store.delete(key);
91
+ else
92
+ store.clear();
93
+ }
94
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/discover/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Discovery — find paid services across catalogs.
3
+ *
4
+ * v0 wires PayAI as the only source. Pay (the MCP catalog) and xona-labs' own
5
+ * registry slot in here as additional source modules later, all returning the
6
+ * same {@link Resource} shape.
7
+ */
8
+ import type { DiscoverOptions, Resource } from "../types.js";
9
+ export { setCacheTtl, invalidate as invalidateCache } from "./cache.js";
10
+ interface InternalDiscoverOptions extends DiscoverOptions {
11
+ catalogs?: Partial<Record<string, string>>;
12
+ }
13
+ /**
14
+ * Fetch resources from each source in parallel, then rank by simple relevance.
15
+ *
16
+ * Ranking v0 is intentionally dumb — token overlap on the URL + metadata. The
17
+ * real moat is in v0.2 ranking (price, reliability, reputation) but we ship
18
+ * with something that works.
19
+ */
20
+ export declare function discover(opts?: InternalDiscoverOptions): Promise<Resource[]>;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discover/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAiB,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5E,OAAO,EAAE,WAAW,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,YAAY,CAAC;AAExE,UAAU,uBAAwB,SAAQ,eAAe;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC5C;AAcD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,IAAI,GAAE,uBAA4B,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsCtF"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Discovery — find paid services across catalogs.
3
+ *
4
+ * v0 wires PayAI as the only source. Pay (the MCP catalog) and xona-labs' own
5
+ * registry slot in here as additional source modules later, all returning the
6
+ * same {@link Resource} shape.
7
+ */
8
+ import { fetchPayAIResources } from "./payai.js";
9
+ import { cached } from "./cache.js";
10
+ export { setCacheTtl, invalidate as invalidateCache } from "./cache.js";
11
+ /**
12
+ * Each source's fetcher runs through the module cache. Cold fetch for the
13
+ * full PayAI catalog is ~50s; cached fetches return in microseconds.
14
+ */
15
+ const sourceFetchers = {
16
+ payai: (endpoint) => cached(`payai:${endpoint ?? "default"}`, () => fetchPayAIResources({ endpoint })),
17
+ // TODO: pay (Solana Foundation catalog), xona (our own listings).
18
+ pay: async () => [],
19
+ xona: async () => [],
20
+ };
21
+ /**
22
+ * Fetch resources from each source in parallel, then rank by simple relevance.
23
+ *
24
+ * Ranking v0 is intentionally dumb — token overlap on the URL + metadata. The
25
+ * real moat is in v0.2 ranking (price, reliability, reputation) but we ship
26
+ * with something that works.
27
+ */
28
+ export async function discover(opts = {}) {
29
+ const sources = opts.sources ?? ["payai"];
30
+ const lists = await Promise.all(sources.map(async (src) => {
31
+ const fetcher = sourceFetchers[src];
32
+ if (!fetcher)
33
+ return [];
34
+ try {
35
+ return await fetcher(opts.catalogs?.[src]);
36
+ }
37
+ catch (err) {
38
+ // One bad source shouldn't kill discovery.
39
+ console.warn(`[xpay] discovery source "${src}" failed:`, err);
40
+ return [];
41
+ }
42
+ }));
43
+ let results = lists.flat();
44
+ // Network filter.
45
+ if (opts.networks?.length) {
46
+ const allowed = new Set(opts.networks);
47
+ results = results.filter((r) => r.accepts.some((a) => allowed.has(normalizeNetwork(a.network))));
48
+ }
49
+ // Query filter + ranking.
50
+ if (opts.query) {
51
+ const terms = opts.query.toLowerCase().split(/\s+/).filter(Boolean);
52
+ results = results
53
+ .map((r) => ({ r, score: scoreResource(r, terms) }))
54
+ .filter((x) => x.score > 0)
55
+ .sort((a, b) => b.score - a.score)
56
+ .map((x) => x.r);
57
+ }
58
+ if (opts.limit)
59
+ results = results.slice(0, opts.limit);
60
+ return results;
61
+ }
62
+ function scoreResource(r, terms) {
63
+ const haystack = [
64
+ r.resource,
65
+ JSON.stringify(r.metadata ?? {}),
66
+ JSON.stringify(r.inputSchema ?? {}),
67
+ ]
68
+ .join(" ")
69
+ .toLowerCase();
70
+ let score = 0;
71
+ for (const t of terms) {
72
+ if (haystack.includes(t))
73
+ score += 1;
74
+ }
75
+ return score;
76
+ }
77
+ function normalizeNetwork(raw) {
78
+ if (raw === "eip155:8453")
79
+ return "base";
80
+ if (raw === "eip155:1")
81
+ return "ethereum";
82
+ if (raw === "eip155:42161")
83
+ return "arbitrum";
84
+ if (raw === "eip155:10")
85
+ return "optimism";
86
+ return raw;
87
+ }
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discover/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * PayAI facilitator client.
3
+ *
4
+ * Source-of-truth for the bulk of xPay's catalog (21k+ x402 resources as of
5
+ * May 2026). The endpoint paginates via `?limit=N&offset=M` and includes a
6
+ * `{ limit, offset, total }` block alongside `items` so we know when to stop.
7
+ */
8
+ import { type Resource } from "../types.js";
9
+ export interface PayAIClientOptions {
10
+ endpoint?: string;
11
+ /** Hard cap on items fetched across all pages. Defaults to the full catalog. */
12
+ maxItems?: number;
13
+ /** Per-page size hint. Capped at {@link PAGE_SIZE}. */
14
+ limit?: number;
15
+ fetch?: typeof fetch;
16
+ }
17
+ export declare function fetchPayAIResources(opts?: PayAIClientOptions): Promise<Resource[]>;
18
+ //# sourceMappingURL=payai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payai.d.ts","sourceRoot":"","sources":["../../src/discover/payai.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAO5D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAOD,wBAAsB,mBAAmB,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CA4C5F"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * PayAI facilitator client.
3
+ *
4
+ * Source-of-truth for the bulk of xPay's catalog (21k+ x402 resources as of
5
+ * May 2026). The endpoint paginates via `?limit=N&offset=M` and includes a
6
+ * `{ limit, offset, total }` block alongside `items` so we know when to stop.
7
+ */
8
+ import { ResourceSchema } from "../types.js";
9
+ const DEFAULT_ENDPOINT = "https://facilitator.payai.network/discovery/resources";
10
+ /** PayAI accepts up to 1000 per call as of testing; a single big page is much
11
+ * cheaper than ten small ones. */
12
+ const PAGE_SIZE = 1000;
13
+ export async function fetchPayAIResources(opts = {}) {
14
+ const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
15
+ const limit = Math.min(opts.limit ?? PAGE_SIZE, PAGE_SIZE);
16
+ const maxItems = opts.maxItems ?? Infinity;
17
+ const fetchImpl = opts.fetch ?? fetch;
18
+ const all = [];
19
+ let offset = 0;
20
+ let total = Infinity;
21
+ // Defensive page cap: even with limit=1000 and a 100k catalog, 100 pages
22
+ // would be excessive. Real-world we stop at `total`.
23
+ for (let page = 0; page < 100 && offset < total && all.length < maxItems; page++) {
24
+ const url = new URL(endpoint);
25
+ url.searchParams.set("limit", String(limit));
26
+ url.searchParams.set("offset", String(offset));
27
+ const res = await fetchImpl(url.toString(), {
28
+ headers: { accept: "application/json" },
29
+ });
30
+ if (!res.ok) {
31
+ throw new Error(`PayAI discovery failed: ${res.status} ${res.statusText}`);
32
+ }
33
+ const body = (await res.json());
34
+ const rawItems = body.items ?? [];
35
+ for (const raw of rawItems) {
36
+ const parsed = ResourceSchema.safeParse(raw);
37
+ if (parsed.success)
38
+ all.push(parsed.data);
39
+ if (all.length >= maxItems)
40
+ return all;
41
+ }
42
+ if (body.pagination) {
43
+ total = body.pagination.total;
44
+ offset = body.pagination.offset + rawItems.length;
45
+ }
46
+ else {
47
+ // No pagination block — assume single-shot.
48
+ break;
49
+ }
50
+ // Server returned fewer items than requested → we hit the end.
51
+ if (rawItems.length < limit)
52
+ break;
53
+ }
54
+ return all;
55
+ }
56
+ //# sourceMappingURL=payai.js.map