@aeon-ai-pay/aigateway 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 (44) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +116 -0
  4. package/bin/cli.mjs +155 -0
  5. package/docs/env-vars.md +73 -0
  6. package/docs/exit-codes.md +65 -0
  7. package/docs/ide-setup.md +60 -0
  8. package/docs/output-schema.md +188 -0
  9. package/docs/recipes/cron-issue-cards.md +69 -0
  10. package/docs/recipes/error-recovery.md +53 -0
  11. package/docs/recipes/integrate-in-agent.md +108 -0
  12. package/docs/recipes/merchant-integration.md +243 -0
  13. package/docs/release-process.md +98 -0
  14. package/docs/troubleshooting.md +200 -0
  15. package/package.json +58 -0
  16. package/scripts/postinstall.mjs +40 -0
  17. package/skills/aigateway/SKILL.md +370 -0
  18. package/skills/aigateway/references/check-status.md +68 -0
  19. package/skills/aigateway/references/create-card.md +114 -0
  20. package/skills/aigateway/references/store.md +87 -0
  21. package/skills/aigateway/references/x402-protocol.md +143 -0
  22. package/src/balance.mjs +92 -0
  23. package/src/commands/clean.mjs +65 -0
  24. package/src/commands/create-card-status.mjs +67 -0
  25. package/src/commands/create-card.mjs +333 -0
  26. package/src/commands/create-image.mjs +428 -0
  27. package/src/commands/wallet-balance.mjs +47 -0
  28. package/src/commands/wallet-gas.mjs +99 -0
  29. package/src/commands/wallet-init.mjs +42 -0
  30. package/src/commands/wallet-topup.mjs +221 -0
  31. package/src/commands/wallet-withdraw.mjs +183 -0
  32. package/src/config.mjs +50 -0
  33. package/src/constants.mjs +22 -0
  34. package/src/error-codes.mjs +50 -0
  35. package/src/funding.mjs +216 -0
  36. package/src/output.mjs +85 -0
  37. package/src/sanitize.mjs +48 -0
  38. package/src/update-check.mjs +69 -0
  39. package/src/walletconnect.mjs +712 -0
  40. package/src/x402.mjs +120 -0
  41. package/templates/cline/.clinerules +53 -0
  42. package/templates/codex/AGENTS.md +56 -0
  43. package/templates/cursor/.cursor/rules/aigateway.mdc +60 -0
  44. package/templates/windsurf/.windsurfrules +48 -0
package/src/x402.mjs ADDED
@@ -0,0 +1,120 @@
1
+ /**
2
+ * x402 协议客户端:初始化 EVM signer + x402Client
3
+ */
4
+ import { x402Client, wrapAxiosWithPayment, x402HTTPClient } from "@aeon-ai-pay/axios";
5
+ import { registerExactEvmScheme } from "@aeon-ai-pay/evm/exact/client";
6
+ import { toClientEvmSigner } from "@aeon-ai-pay/evm";
7
+ import { privateKeyToAccount } from "viem/accounts";
8
+ import { createWalletClient, http, publicActions, formatUnits } from "viem";
9
+ import { bsc } from "viem/chains";
10
+ import { BSC_RPC_URL } from "./constants.mjs";
11
+ import axios from "axios";
12
+
13
+ /**
14
+ * 创建已注册 EVM 签名的 x402 axios 客户端
15
+ * @param {`0x${string}`} privateKey - EVM 私钥
16
+ * @returns {{ api: AxiosInstance, client: x402Client, address: string, getOrderNo: () => string|null }}
17
+ */
18
+ export function createX402Api(privateKey) {
19
+ const evmAccount = privateKeyToAccount(privateKey);
20
+ const walletClient = createWalletClient({
21
+ account: evmAccount,
22
+ chain: bsc,
23
+ transport: http(BSC_RPC_URL),
24
+ }).extend(publicActions);
25
+
26
+ const evmSigner = toClientEvmSigner({
27
+ address: evmAccount.address,
28
+ signTypedData: (message) => evmAccount.signTypedData(message),
29
+ readContract: (args) =>
30
+ walletClient.readContract({ ...args, args: args.args || [] }),
31
+ sendTransaction: (args) =>
32
+ walletClient.sendTransaction({ to: args.to, data: args.data }),
33
+ waitForTransactionReceipt: (args) =>
34
+ walletClient.waitForTransactionReceipt(args),
35
+ });
36
+
37
+ const client = new x402Client();
38
+ registerExactEvmScheme(client, { signer: evmSigner });
39
+
40
+ const axiosInstance = axios.create();
41
+
42
+ // 在 wrapAxiosWithPayment 之前注册拦截器,
43
+ // 从 402 响应体中捕获 orderNo(服务端在 firstRequest 返回)
44
+ let capturedOrderNo = null;
45
+ axiosInstance.interceptors.response.use(
46
+ (response) => response,
47
+ (error) => {
48
+ if (error.response?.status === 402 && error.response?.data?.orderNo) {
49
+ capturedOrderNo = error.response.data.orderNo;
50
+ }
51
+ return Promise.reject(error);
52
+ }
53
+ );
54
+
55
+ const api = wrapAxiosWithPayment(axiosInstance, client);
56
+
57
+ return {
58
+ api,
59
+ client,
60
+ address: evmAccount.address,
61
+ getOrderNo: () => capturedOrderNo,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * 第一次发起 x402 请求(不带签名),从 402 响应中提取实际付款要求。
67
+ * 同时保留完整的 402 响应数据和原始请求配置,供后续手动签名使用。
68
+ * 字段名与 x402 v2 PaymentRequirements 标准对齐:asset、payTo、amount。
69
+ *
70
+ * 兼容 GET(card 路径)和 POST(image / Skill Boss 路径)。
71
+ *
72
+ * @param {string} url
73
+ * @param {{ method?: "GET"|"POST", data?: any, headers?: object }} [options]
74
+ * @returns {Promise<{amountUsdt: number, amountWei: string, decimals: number, asset: string, payTo: string, orderNo: string|null, raw402Response: object, requestConfig: object}>}
75
+ */
76
+ export async function fetchPaymentRequirements(url, options = {}) {
77
+ const rawClient = axios.create();
78
+ const method = (options.method || "GET").toUpperCase();
79
+ try {
80
+ if (method === "POST") {
81
+ await rawClient.post(url, options.data, { headers: options.headers });
82
+ } else {
83
+ await rawClient.get(url, { headers: options.headers });
84
+ }
85
+ throw new Error("Expected HTTP 402 but got 200");
86
+ } catch (err) {
87
+ if (err.response?.status !== 402) throw err;
88
+ const data = err.response.data;
89
+ const accept = data?.accepts?.[0];
90
+ if (!accept) throw new Error("No payment requirements in 402 response");
91
+ const decimals = accept.tokenDecimals || 18;
92
+ const amountWei = BigInt(accept.amount);
93
+ const amountUsdt = parseFloat(formatUnits(amountWei, decimals));
94
+ return {
95
+ amountUsdt,
96
+ amountWei: amountWei.toString(),
97
+ decimals,
98
+ asset: accept.asset,
99
+ payTo: accept.payTo,
100
+ orderNo: data.orderNo || null,
101
+ raw402Response: err.response,
102
+ requestConfig: err.config,
103
+ };
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 从响应头中解码 PAYMENT-RESPONSE(x402 v2)
109
+ * @param {object} headers - axios response headers
110
+ * @returns {object|null}
111
+ */
112
+ export function decodePaymentResponse(headers) {
113
+ const raw = headers["payment-response"] || headers["PAYMENT-RESPONSE"];
114
+ if (!raw) return null;
115
+ try {
116
+ return JSON.parse(Buffer.from(raw, "base64").toString("utf-8"));
117
+ } catch {
118
+ return { raw };
119
+ }
120
+ }
@@ -0,0 +1,53 @@
1
+ # aigateway — Virtual Card via x402
2
+
3
+ Trigger this rule when the user wants to create / query / manage one-time virtual debit cards funded via USDT on BSC.
4
+
5
+ ## Setup (run once)
6
+
7
+ ```bash
8
+ aigateway wallet-init
9
+ ```
10
+
11
+ `envelope.data.ready === true` means ready.
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ aigateway create-card --amount <USD> --poll # Create card, poll until ready
17
+ aigateway create-card --amount <USD> --app-id <merchantId> --poll # With merchant app ID
18
+ aigateway create-card-status --order-no <orderNo> # Single status query
19
+ aigateway create-card-status --order-no <orderNo> --poll # Poll until terminal
20
+ aigateway wallet-balance # Show local wallet balance
21
+ aigateway wallet-topup --amount <USDT> # WalletConnect: USDT top-up
22
+ aigateway wallet-gas --amount <BNB> # WalletConnect: BNB gas top-up
23
+ aigateway wallet-withdraw [--to 0x...] [--amount <USDT>] # Reclaim funds
24
+ ```
25
+
26
+ ## Envelope
27
+
28
+ stdout = one line of JSON.
29
+
30
+ - Success: `{ "ok": true, "command": ..., "data": { ... } }`
31
+ - Failure: `{ "ok": false, "command": ..., "error": { "code": ..., "message": ..., ... } }`
32
+
33
+ Branch on `error.code`. Exit codes: `0`/`1`/`2`/`3`/`4` = success/user/timeout/service/internal.
34
+
35
+ ## Hard Rules
36
+
37
+ - No private keys from the user.
38
+ - No full card numbers, CVV, or expiry in output (already sanitized).
39
+ - WalletConnect-opening commands (`create-card` / `create-image` when underfunded / `wallet-init` / `wallet-gas`) must run in foreground.
40
+ - No auto-retry on `PAYMENT_REJECTED` or `PAYMENT_TIMEOUT`.
41
+
42
+ ## Recovery Cheatsheet
43
+
44
+ | Code | Action |
45
+ | --- | --- |
46
+ | `AMOUNT_OUT_OF_RANGE` | Show `error.min`/`error.max`; ask user. |
47
+ | `INSUFFICIENT_USDT` | `aigateway wallet-init` then retry. |
48
+ | `INSUFFICIENT_BNB` | `aigateway wallet-gas` then retry. |
49
+ | `POLL_TIMEOUT` | Note `orderNo`; query later. |
50
+ | `PAYMENT_REJECTED` / `PAYMENT_TIMEOUT` | Ask user before retrying. |
51
+ | `SERVICE_UNAVAILABLE` | Exponential backoff retry. |
52
+
53
+ Full reference: `docs/output-schema.md`, `docs/exit-codes.md`, `docs/recipes/`.
@@ -0,0 +1,56 @@
1
+ # aigateway — Virtual Card Agent Rules
2
+
3
+ When a user asks to **create, query, or manage** a one-time virtual debit card funded with USDT on BSC, use the `aigateway` CLI as the source of truth.
4
+
5
+ ## Setup (run once per environment)
6
+
7
+ ```bash
8
+ aigateway wallet-init
9
+ ```
10
+
11
+ Returns an envelope on stdout; `envelope.data.ready === true` means ready.
12
+
13
+ ## Command Surface
14
+
15
+ ```bash
16
+ aigateway create-card --amount <USD> [--app-id <merchantId>] --poll # Create card
17
+ aigateway create-card-status --order-no <orderNo> [--poll] # Query status
18
+ aigateway wallet-balance # Local balance
19
+ aigateway wallet-topup --amount <USDT> # USDT top-up via WalletConnect
20
+ aigateway wallet-gas --amount <BNB> # BNB top-up via WalletConnect
21
+ aigateway wallet-withdraw [--to 0x...] [--amount <USDT>] # Reclaim funds
22
+ ```
23
+
24
+ `--app-id` defaults to `TEST000001`. `--quiet` suppresses stderr noise. `--legacy-output` swaps the envelope for the old pre-envelope JSON shape.
25
+
26
+ ## Output Contract
27
+
28
+ Every command emits **one line of JSON** to stdout (the envelope):
29
+
30
+ - `{ "ok": true, "command": "...", "data": { ... } }`
31
+ - `{ "ok": false, "command": "...", "error": { "code": "...", "message": "...", ... } }`
32
+
33
+ Stderr is human-readable progress. Match on `error.code` (stable), not on `error.message`.
34
+
35
+ Exit codes: `0` success · `1` user · `2` timeout · `3` service/network · `4` internal.
36
+
37
+ ## Hard Rules
38
+
39
+ - **Never** prompt for private keys. The CLI auto-generates a local session wallet.
40
+ - **Never** display full card numbers, CVV, or expiry. The CLI already redacts these to `•••• 1234`.
41
+ - **Never** run `create-card` / `create-image` / `wallet-init` / `wallet-gas` in the background — they open a WalletConnect QR window.
42
+ - **Never** auto-retry `PAYMENT_REJECTED` or `PAYMENT_TIMEOUT`. Ask the user.
43
+
44
+ ## Error Recovery (high-frequency cases)
45
+
46
+ | `error.code` | Action |
47
+ | ------------ | ------ |
48
+ | `AMOUNT_OUT_OF_RANGE` | Show `error.min` / `error.max`; re-prompt user. |
49
+ | `INSUFFICIENT_USDT` | Run `aigateway wallet-init`, then re-run create. |
50
+ | `INSUFFICIENT_BNB` | Run `aigateway wallet-gas`, then re-run the failing op. |
51
+ | `POLL_TIMEOUT` | Card may still be provisioning. Note `error.orderNo`. |
52
+ | `PAYMENT_REJECTED` | User cancelled. Ask before retrying. |
53
+ | `PAYMENT_TIMEOUT` | 5-minute approval window expired. Ask before retrying. |
54
+ | `SERVICE_UNAVAILABLE` | Retry with exponential backoff (1s → 4s → 16s, max 3). |
55
+
56
+ Full schema and recipes: see `docs/output-schema.md`, `docs/exit-codes.md`, and `docs/recipes/`.
@@ -0,0 +1,60 @@
1
+ ---
2
+ description: Use the aigateway CLI to create and manage one-time virtual debit cards via the x402 payment protocol on BSC. Trigger on intents like "create a card", "buy a virtual card", "card status", "top up", "withdraw funds".
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # aigateway — Virtual Card via x402
7
+
8
+ Use this rule when the user wants to **create, query, or manage** one-time-use virtual debit cards funded via USDT on BSC.
9
+
10
+ ## First-time setup (always run once)
11
+
12
+ ```bash
13
+ aigateway wallet-init
14
+ ```
15
+
16
+ Auto-creates a local session wallet if missing. Returns an envelope; `envelope.data.ready === true` means ready.
17
+
18
+ ## Commands
19
+
20
+ | Intent | Command |
21
+ | --- | --- |
22
+ | Create a $N card and poll until ready | `aigateway create-card --amount <N> --poll` |
23
+ | Specify merchant app ID (optional) | `aigateway create-card --amount <N> --app-id <merchantId> --poll` |
24
+ | Check status of an existing order | `aigateway create-card-status --order-no <orderNo>` |
25
+ | Show local wallet balance | `aigateway wallet-balance` |
26
+ | Top up USDT from main wallet (interactive WalletConnect) | `aigateway wallet-topup --amount <USDT>` |
27
+ | Top up BNB for gas (interactive WalletConnect) | `aigateway wallet-gas --amount <BNB>` |
28
+ | Withdraw funds back to main wallet | `aigateway wallet-withdraw [--to 0x...] [--amount <USDT>]` |
29
+
30
+ ## Output Contract
31
+
32
+ Every command writes **one line of JSON** to stdout: the envelope.
33
+
34
+ - Success: `{ "ok": true, "command": "...", "data": { ... } }`
35
+ - Failure: `{ "ok": false, "command": "...", "error": { "code": "...", "message": "...", ... } }`
36
+
37
+ Stderr is human-readable progress; pass `--quiet` if you want it suppressed.
38
+
39
+ ## Error Handling
40
+
41
+ Branch on `error.code` (stable). Common cases:
42
+
43
+ - `AMOUNT_OUT_OF_RANGE` — Show valid range from `error.min` / `error.max`, ask user.
44
+ - `INSUFFICIENT_USDT` / `INSUFFICIENT_BNB` — Run `aigateway wallet-init` / `aigateway wallet-gas` then retry.
45
+ - `PAYMENT_REJECTED` / `PAYMENT_TIMEOUT` — User cancelled or didn't respond. **Do not auto-retry**; ask first.
46
+ - `POLL_TIMEOUT` — Card may still be provisioning. Note the `orderNo`, query later.
47
+ - Exit codes: `0` success, `1` user error, `2` timeout, `3` service/network, `4` internal.
48
+
49
+ ## Hard Rules
50
+
51
+ - **Never** ask the user for a private key — the local wallet is auto-generated.
52
+ - **Never** display the full card number, CVV, or expiry. Output already redacts these to `•••• 1234`.
53
+ - **Never** run `create-card` / `create-image` / `wallet-init` / `wallet-gas` in the background — they may open a WalletConnect QR window that needs user attention.
54
+ - **Never** auto-retry rejected / timed-out signatures.
55
+
56
+ ## Full Reference
57
+
58
+ - Envelope schema: `docs/output-schema.md`
59
+ - Exit / error codes: `docs/exit-codes.md`
60
+ - Integration recipes: `docs/recipes/`
@@ -0,0 +1,48 @@
1
+ # aigateway — Virtual Card via x402
2
+
3
+ When the user wants to create / query / manage a one-time virtual debit card funded with USDT on BSC, use the `aigateway` CLI.
4
+
5
+ ## First-time setup (always run once)
6
+
7
+ ```bash
8
+ aigateway wallet-init
9
+ ```
10
+
11
+ `envelope.data.ready === true` means ready.
12
+
13
+ ## Commands
14
+
15
+ | Intent | Command |
16
+ | --- | --- |
17
+ | Create a $N card and poll until ready | `aigateway create-card --amount <N> --poll` |
18
+ | Specify merchant app ID | `aigateway create-card --amount <N> --app-id <merchantId> --poll` |
19
+ | Check status of an existing order | `aigateway create-card-status --order-no <orderNo>` |
20
+ | Show local wallet balance | `aigateway wallet-balance` |
21
+ | Top up USDT (interactive WalletConnect) | `aigateway wallet-topup --amount <USDT>` |
22
+ | Top up BNB for gas (interactive WalletConnect) | `aigateway wallet-gas --amount <BNB>` |
23
+ | Withdraw funds back to main wallet | `aigateway wallet-withdraw [--to 0x...] [--amount <USDT>]` |
24
+
25
+ ## Output Contract
26
+
27
+ Every command emits one line of JSON to stdout (the envelope):
28
+
29
+ - Success: `{ "ok": true, "command": "...", "data": { ... } }`
30
+ - Failure: `{ "ok": false, "command": "...", "error": { "code": "...", "message": "...", ... } }`
31
+
32
+ Branch on `error.code` (stable). Exit codes: `0` success, `1` user, `2` timeout, `3` service/network, `4` internal.
33
+
34
+ ## Hard Rules
35
+
36
+ - Never ask for a private key. The local wallet is auto-generated.
37
+ - Never display full card numbers, CVV, or expiry — output is already sanitized.
38
+ - Never run `create-card` / `create-image` / `wallet-init` / `wallet-gas` in the background; they open a QR window requiring user attention.
39
+ - Never auto-retry `PAYMENT_REJECTED` / `PAYMENT_TIMEOUT`.
40
+
41
+ ## Common Recovery
42
+
43
+ - `AMOUNT_OUT_OF_RANGE` → Show `error.min` / `error.max`, ask for a new amount.
44
+ - `INSUFFICIENT_USDT` → Run `aigateway wallet-init`, then retry create.
45
+ - `INSUFFICIENT_BNB` → Run `aigateway wallet-gas`, then retry the failing op.
46
+ - `POLL_TIMEOUT` → Note `orderNo`, ask user to query later with `aigateway create-card-status --order-no <orderNo>`.
47
+
48
+ Full reference: `docs/output-schema.md`, `docs/exit-codes.md`, `docs/recipes/`.