@aeon-ai-pay/aigateway 0.1.3 → 0.1.5

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/CHANGELOG.md CHANGED
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.5] — 2026-05-19
11
+
12
+ ### Changed
13
+ - **Removed all Chinese-language content from source files**. All comments,
14
+ docstrings, log messages, and SKILL.md guidance are now English-only.
15
+ `normalizeWalletError` no longer matches CJK error strings from localised
16
+ wallet apps; those fall through to the generic `WALLET_ERROR` code.
17
+ - SKILL.md's "Wording Discipline" section is now language-neutral: instead
18
+ of listing Chinese phrasings, it instructs translators to keep the "wallet
19
+ top-up" verb / noun lexically distinct from the "card face value" verb /
20
+ noun in every target language.
21
+
22
+ ### Removed
23
+ - `test/create-logic.test.mjs` — stale unit tests inherited from `aicard`
24
+ that referenced internal functions removed during the merge (e.g.
25
+ `inlineWalletConnectTopup`, now replaced by `funding.mjs::fundSessionKey`).
26
+ The file was never published to npm (`test/` is not in `files`).
27
+
28
+ ## [0.1.4] — 2026-05-19
29
+
30
+ ### Added
31
+ - **`create-card` envelope now carries a `balance` block** (`initial`, `before`,
32
+ `after`, `charged`, `topup`) parallel to `create-image`, so the agent can
33
+ render the same money-flow narrative for card issuance.
34
+
35
+ ### Changed
36
+ - **Both paid commands adopt an emoji-aligned card-style success template**
37
+ with explicit balance transitions (`{initial} → {before}` for top-up;
38
+ `{before} → {after}` for charge) on dedicated rows. The `💸 Top-up` row is
39
+ conditional — only rendered when `balance.topup` is non-null.
40
+ - `create-card`: header `✅ Card Issued`, rows for Order / Card / State /
41
+ Face value / Usage / Tx / (optional) Top-up / Charged.
42
+ - `create-image`: header `✅ Generated`, second row `🧩 Powered by Skillboss`,
43
+ rows for Path / Format / Dimensions / Size / Tx / (optional) Top-up /
44
+ Charged.
45
+ - SKILL.md Copy Constraints table extended with all new template rows so
46
+ agents must reproduce the glyphs (`→`, `−`, `+`) exactly.
47
+
10
48
  ## [0.1.3] — 2026-05-19
11
49
 
12
50
  ### Fixed
@@ -24,14 +62,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
24
62
  ## [0.1.2] — 2026-05-19
25
63
 
26
64
  ### Changed
27
- - **SKILL.md wording discipline**: `wallet-topup` ask and `create-card` ask now have
28
- enforced lexical separation. Step 2 (`wallet-topup`) explicitly says "load USDT
29
- **into your session wallet**" / "往**本地钱包**充值 USDT". Step 3a (`create-card`)
30
- says "card **face value**" / "**开多少美元的卡** / 卡的面额", and explicitly
31
- forbids translating it as "充值". Fixes a UX confusion where a user with a
32
- funded wallet would be asked "请问您想充值多少美元到虚拟卡上?" and reasonably
33
- wonder if it was another wallet top-up prompt.
34
- - New "Wording Discipline" section in SKILL.md `## Copy Constraints` to lock this in.
65
+ - **SKILL.md wording discipline**: the `wallet-topup` and `create-card` prompts
66
+ now enforce strict lexical separation. Step 2 (`wallet-topup`) says "load USDT
67
+ **into your session wallet**"; Step 3a (`create-card`) says "card
68
+ **face value**" / "issue a card with how much". When translating to any
69
+ non-English language, the verb / noun for each concept must remain distinct
70
+ so users cannot conflate them.
71
+ - New "Wording Discipline" section in SKILL.md `## Copy Constraints` to lock
72
+ this in.
35
73
 
36
74
  ## [0.1.1] — 2026-05-19
37
75
 
package/bin/cli.mjs CHANGED
@@ -7,8 +7,9 @@ if (major < 25) {
7
7
  process.exit(1);
8
8
  }
9
9
 
10
- // WalletConnect v2 SDK 已知缺陷:relay 偶发 null WebSocket 帧导致
11
- // isJsonRpcPayload 内部 'id' in null TypeError,不影响业务流程,静默忽略
10
+ // Known WalletConnect v2 SDK quirk: the relay occasionally emits null WebSocket frames,
11
+ // causing `'id' in null` inside isJsonRpcPayload to throw a TypeError. It does not
12
+ // affect business logic, so silently ignore it.
12
13
  process.on("uncaughtException", (err) => {
13
14
  if (
14
15
  err instanceof TypeError &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeon-ai-pay/aigateway",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "AI Agents discover, invoke, and settle paid LLMs, APIs, and Skills — starting with Skill Boss. No manual key setup. No prepayment. Pay-per-call via x402 or Agent Card.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * npm install -g 后自动安装 skill 到所有已检测的 AI 编码工具
4
+ * After `npm install -g`, automatically install the skill into every detected AI coding tool.
5
5
  *
6
- * 优先使用 `npx skills add` (Vercel Labs) 统一安装到所有工具
7
- * 失败时 fallback 到手动复制 Claude Code skills 目录
6
+ * Prefer `npx skills add` (Vercel Labs) so the skill is registered across all tools at once.
7
+ * If that fails, fall back to manually copying the skill into the Claude Code skills directory.
8
8
  */
9
9
 
10
10
  import { cpSync, existsSync, mkdirSync } from 'node:fs';
@@ -20,7 +20,7 @@ if (!existsSync(skillSrc)) {
20
20
  process.exit(0);
21
21
  }
22
22
 
23
- // 尝试用 skills CLI 安装到所有工具
23
+ // Try installing into every tool via the skills CLI
24
24
  try {
25
25
  execFileSync('npx', ['skills', 'add', skillSrc, '-g', '-y', '--copy'], {
26
26
  stdio: 'inherit',
@@ -30,10 +30,10 @@ try {
30
30
  console.log('✔ aigateway skill installed via skills CLI (all detected tools)');
31
31
  process.exit(0);
32
32
  } catch {
33
- // skills CLI 不可用或失败,fallback
33
+ // skills CLI unavailable or failed — fall back
34
34
  }
35
35
 
36
- // Fallback: 手动复制到 Claude Code
36
+ // Fallback: copy manually into Claude Code
37
37
  const dest = join(homedir(), '.claude', 'skills', 'aigateway');
38
38
  mkdirSync(dirname(dest), { recursive: true });
39
39
  cpSync(skillSrc, dest, { recursive: true, force: true });
@@ -16,7 +16,7 @@ description: >
16
16
  emoji: "🛰️"
17
17
  homepage: https://github.com/AEON-Project/aigateway
18
18
  metadata:
19
- version: "0.1.3"
19
+ version: "0.1.5"
20
20
  author: AEON-Project
21
21
  openclaw:
22
22
  requires:
@@ -163,12 +163,10 @@ Trigger: `wallet-init` envelope reports `needsTopup: true` (any of `no_prior_fun
163
163
  This is a **session-wallet top-up** in USDT (NOT card face value). Make the wording unambiguously about the wallet to avoid the user confusing it with the card face value asked in Step 3a.
164
164
 
165
165
  - Presets: **5 / 10 / 20 / 50 USDT**. Custom amounts must be ≥ 5 USDT.
166
- - Ask the user **before** running the command. **Make "wallet" / "钱包" explicit in the question:**
166
+ - Ask the user **before** running the command. **Make the noun "wallet" explicit in the question** (when translating to other languages, the translated noun must also unambiguously mean *session wallet*, never *card*):
167
167
 
168
168
  > How much USDT would you like to load **into your session wallet**? (presets: 5 / 10 / 20 / 50, or any custom amount ≥ 5)
169
169
 
170
- (Suggested Chinese phrasing: "请问要往**本地钱包**充值多少 USDT?预设 5 / 10 / 20 / 50,或自定义 ≥ 5。")
171
-
172
170
  - Once the user picks an amount, run:
173
171
 
174
172
  ```bash
@@ -211,13 +209,10 @@ Trigger: user wants to **buy / create / get a virtual card** *and* Step 1 envelo
211
209
 
212
210
  ### Amount confirmation
213
211
 
214
- This is the **card face value** the user wants to issue (NOT a wallet top-up). Amount must be in `amountLimits.min ~ amountLimits.max` (from Step 1; never hardcode). If user did not specify, ask once — **use the word "card face value" / "面额", never "充值" / "top up"** to avoid confusing it with the `wallet-topup` step:
212
+ This is the **card face value** the user wants to issue (NOT a wallet top-up). Amount must be in `amountLimits.min ~ amountLimits.max` (from Step 1; never hardcode). If the user did not specify a value, ask once — use the phrase **"card face value"** (or in any non-English language, the equivalent for *the amount loaded onto a new card*), never the verb used for *wallet top-up*:
215
213
 
216
214
  > What card face value would you like to issue? Allowed range: ${min}~${max} USD.
217
215
 
218
- (Suggested Chinese phrasing: "请问要开多少美元的卡?允许范围 $${min} ~ $${max}。"
219
- **Do not** translate this as "充值多少美元" — that wording belongs to `wallet-topup` and confuses users.)
220
-
221
216
  Once specified, **execute immediately**.
222
217
 
223
218
  ### Execute
@@ -238,16 +233,30 @@ Output template first line:
238
233
 
239
234
  ### Success
240
235
 
241
- `envelope.data`: `{ orderNo, data, paymentResponse, pollResult? }`. After fetching details (may take ~30 s), display **verbatim**:
236
+ `envelope.data`: `{ orderNo, amount, data, paymentResponse, balance: { initial, before, after, charged, topup }, pollResult? }`.
237
+
238
+ After fetching details (may take ~30 s), display **verbatim** (emoji, spacing, glyphs `→` / `−` / `+` must match exactly):
242
239
 
243
240
  ```
244
- Order No: {orderNo}
245
- Card: {cardScheme} •••• {last4}
246
- State: Active
247
- Remaining balance: ${amount} USD
248
- Usage: 0 / 1 (single-use)
241
+ Card Issued
242
+ 🆔 Order {orderNo}
243
+ 💳 Card {cardScheme} •••• {last4}
244
+ 🎯 State Active
245
+ 💵 Face value ${amount} USD
246
+ 🔢 Usage 0 / 1 (single-use)
247
+ 🔗 Tx {transaction}
248
+ 💸 Top-up {initial} → {before} USDT (+{topup})
249
+ 💰 Charged {before} → {after} USDT (−{charged})
249
250
  ```
250
251
 
252
+ **Field rules:**
253
+
254
+ - `{cardScheme}` and `••••{last4}` come from `data.data.model` (already sanitized — never show full card number).
255
+ - `{transaction}` is `data.paymentResponse.txHash` or `data.data.transaction`. If absent, render the line as `🔗 Tx —`.
256
+ - The **`💸 Top-up`** row is **conditional**: render only when `data.balance.topup` is non-null and non-zero (i.e. a lazy top-up actually happened during this call). Otherwise **omit the entire `💸 Top-up` line**.
257
+ - The **`💰 Charged`** row is always rendered.
258
+ - Use the minus sign character `−` (U+2212) before `{charged}`, not the hyphen `-`. Use `→` (U+2192) for the balance transition arrow.
259
+
251
260
  Always record `orderNo` — only identifier for status queries.
252
261
 
253
262
  ### Errors
@@ -284,19 +293,31 @@ Output template first line:
284
293
 
285
294
  ### Success
286
295
 
287
- `envelope.data`: `{ prompt, transaction, images: [{ url, localPath, format, width, height, sizeHuman }], balance: { initial, before, after, charged } }`.
296
+ `envelope.data`: `{ prompt, transaction, images: [{ url, localPath, format, width, height, sizeHuman }], balance: { initial, before, after, charged, topup } }`.
288
297
 
289
- Display **verbatim**:
298
+ Display **verbatim** (emoji, spacing, dash glyphs `→` / `−` / `+` must match exactly):
290
299
 
291
300
  ```
292
301
  ✅ Generated
293
- Prompt: {prompt}
294
- Image: {localPath} ({width}×{height}, {sizeHuman})
295
- Transaction: {transaction}
296
- Charged: {balance.charged} USDT
297
- Balance: {balance.after} USDT remaining
302
+ 🧩 Powered by Skillboss
303
+ 📁 Path {localPath}
304
+ 🎨 Format {FORMAT}
305
+ 📐 Dimensions {width} × {height}
306
+ 💾 Size {sizeHuman}
307
+ 🔗 Tx {transaction}
308
+ 💸 Top-up {initial} → {before} USDT (+{topup})
309
+ 💰 Charged {before} → {after} USDT (−{charged})
298
310
  ```
299
311
 
312
+ **Field rules:**
313
+
314
+ - `{FORMAT}` is `data.outputFormat` uppercased (e.g. `PNG`, `JPEG`, `WEBP`).
315
+ - `{width}` / `{height}` / `{sizeHuman}` come from `data.images[0]` (first image only — agent does not list extras unless asked).
316
+ - `{transaction}` is `data.transaction` (may be `null` if the server didn't return one; in that case render the line as `🔗 Tx —`).
317
+ - The **`💸 Top-up`** line is **conditional**: only render it if `data.balance.topup` is not null and not "0" (i.e. a lazy top-up actually happened during this call). Otherwise **omit the entire `💸 Top-up` line**.
318
+ - The **`💰 Charged`** line is always rendered.
319
+ - Use the minus sign character `−` (U+2212) before `{charged}`, not the hyphen `-`. Use `→` (U+2192) for the balance transition arrow.
320
+
300
321
  ### Errors
301
322
 
302
323
  | `error.code` | Action |
@@ -404,6 +425,15 @@ The following first-line / key-phrase strings must be **exactly reproduced** —
404
425
  | Pre-check | `> Pre-check in progress...` |
405
426
  | Top up | `> Topping up wallet...` |
406
427
  | Create card | `> Creating Agent Card...` |
428
+ | Card success header | `✅ Card Issued` |
429
+ | Card Order row | `🆔 Order {orderNo}` |
430
+ | Card scheme row | `💳 Card {cardScheme} •••• {last4}` |
431
+ | Card State row | `🎯 State Active` |
432
+ | Card Face value row | `💵 Face value ${amount} USD` |
433
+ | Card Usage row | `🔢 Usage 0 / 1 (single-use)` |
434
+ | Card Tx row | `🔗 Tx {transaction}` |
435
+ | Card Top-up row (conditional) | `💸 Top-up {initial} → {before} USDT (+{topup})` |
436
+ | Card Charged row | `💰 Charged {before} → {after} USDT (−{charged})` |
407
437
  | Create image | `> Generating image...` |
408
438
  | Fetch details | `> Fetching card details, please wait...` |
409
439
  | Query status | `> Fetching card status...` |
@@ -411,17 +441,25 @@ The following first-line / key-phrase strings must be **exactly reproduced** —
411
441
  | Withdraw target line | `To: main wallet (0x0...{last4})` |
412
442
  | Withdraw status line | `Status: completed` |
413
443
  | Image success header | `✅ Generated` |
444
+ | Image success row 2 | `🧩 Powered by Skillboss` |
445
+ | Image Path row | `📁 Path {localPath}` |
446
+ | Image Format row | `🎨 Format {FORMAT}` |
447
+ | Image Dimensions row | `📐 Dimensions {width} × {height}` |
448
+ | Image Size row | `💾 Size {sizeHuman}` |
449
+ | Image Tx row | `🔗 Tx {transaction}` |
450
+ | Image Top-up row (conditional) | `💸 Top-up {initial} → {before} USDT (+{topup})` |
451
+ | Image Charged row | `💰 Charged {before} → {after} USDT (−{charged})` |
414
452
  | Wallet prepared header | `✅ Wallet prepared` |
415
453
 
416
454
  Address rendering: always `0x0...{last4}` (first 3 + ellipsis + last 4 chars).
417
455
 
418
456
  ### Wording Discipline: "wallet top-up" vs "card face value"
419
457
 
420
- These two amounts are **different concepts** asked at **different steps**. Translators must keep them lexically distinct so users don't conflate them:
458
+ These two amounts are **different concepts** asked at **different steps**. Translators (to any non-English language) must keep them **lexically distinct** so users do not conflate them:
421
459
 
422
- | Step | Concept | Required wording (English) | Suggested Chinese | Forbidden mix-ups |
423
- | --- | --- | --- | --- | --- |
424
- | Step 2 (`wallet-topup`) | USDT into the **session wallet** | "load USDT **into your session wallet**" | "往**本地钱包**充值 USDT" | Don't say "充值到卡里" / "load onto the card" — that's Step 3a |
425
- | Step 3a (`create-card`) | USD **face value** loaded onto a new card | "card **face value**" / "issue a card with how much" | "**开多少美元的卡** / 卡的面额" | Don't say "充值多少" / "充值到卡" without strong "card" qualifier — confuses with Step 2 |
460
+ | Step | Concept | Required English wording | Forbidden mix-ups |
461
+ | --- | --- | --- | --- |
462
+ | Step 2 (`wallet-topup`) | USDT into the **session wallet** | "load USDT **into your session wallet**" | Never say "load onto the card" — that's Step 3a |
463
+ | Step 3a (`create-card`) | USD **face value** loaded onto a new card | "card **face value**" / "issue a card with how much" | Never use the same verb you used for *wallet top-up* without a strong "card" qualifier |
426
464
 
427
- **Rule of thumb**: if the agent's question contains the word "充值" (top up), the noun *must* be "钱包" (wallet); if the question is about a card's amount, prefer "**面额**" or "**开多少美元的卡**", never "充值".
465
+ **Rule of thumb**: if a question is about the *wallet* (Step 2), the target noun must mean *session wallet*. If a question is about a *card amount* (Step 3a), prefer wording that means *face value* / *the amount loaded onto a new card*. The verb for one concept must never be reused for the other concept in the same language.
package/src/balance.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 钱包余额查询(共享模块)
2
+ * Wallet balance lookup (shared module)
3
3
  */
4
4
  import { privateKeyToAccount } from "viem/accounts";
5
5
  import { createPublicClient, http, formatUnits } from "viem";
@@ -42,8 +42,8 @@ function getClient() {
42
42
  }
43
43
 
44
44
  /**
45
- * 通过地址查询 BNB USDT 余额(不需要私钥)
46
- * @param {string} address - EVM 地址
45
+ * Query BNB and USDT balance by address (no private key required).
46
+ * @param {string} address - EVM address
47
47
  */
48
48
  export async function getBalanceByAddress(address) {
49
49
  const client = getClient();
@@ -68,7 +68,7 @@ export async function getBalanceByAddress(address) {
68
68
  }
69
69
 
70
70
  /**
71
- * 通过私钥查询钱包 BNB USDT 余额
71
+ * Query a wallet's BNB and USDT balance by private key.
72
72
  * @param {string} privateKey
73
73
  */
74
74
  export async function getWalletBalance(privateKey) {
@@ -77,9 +77,9 @@ export async function getWalletBalance(privateKey) {
77
77
  }
78
78
 
79
79
  /**
80
- * 查询 session key facilitator USDT allowance
81
- * @param {string} ownerAddress - session key 地址
82
- * @returns {bigint} 当前 allowancewei
80
+ * Query the session key's USDT allowance for the facilitator.
81
+ * @param {string} ownerAddress - session key address
82
+ * @returns {bigint} current allowance (in wei)
83
83
  */
84
84
  export async function getAllowance(ownerAddress) {
85
85
  const client = getClient();
@@ -8,7 +8,7 @@ export async function clean() {
8
8
  const home = homedir();
9
9
  const removed = [];
10
10
 
11
- // 1. skills CLI 移除(覆盖所有工具)
11
+ // 1. Remove via the skills CLI (covers every tool)
12
12
  try {
13
13
  execFileSync("npx", ["skills", "remove", "aigateway", "-g", "-y"], {
14
14
  stdio: "inherit",
@@ -17,7 +17,7 @@ export async function clean() {
17
17
  logInfo("Removed aigateway skill via skills CLI");
18
18
  removed.push("skills");
19
19
  } catch {
20
- // skills CLI 不可用,手动清理 Claude Code
20
+ // skills CLI unavailable — clean Claude Code manually
21
21
  const skillDir = join(home, ".claude", "skills", "aigateway");
22
22
  if (existsSync(skillDir)) {
23
23
  rmSync(skillDir, { recursive: true, force: true });
@@ -26,7 +26,7 @@ export async function clean() {
26
26
  }
27
27
  }
28
28
 
29
- // 2. 卸载全局包
29
+ // 2. Uninstall the global package
30
30
  try {
31
31
  execFileSync("npm", ["uninstall", "-g", "@aeon-ai-pay/aigateway"], {
32
32
  stdio: "inherit",
@@ -38,7 +38,7 @@ export async function clean() {
38
38
  logInfo("Global package not installed, skipping uninstall");
39
39
  }
40
40
 
41
- // 3. 清理 npm 缓存
41
+ // 3. Clean npm cache
42
42
  try {
43
43
  execFileSync("npm", ["cache", "clean", "--force"], {
44
44
  stdio: "inherit",
@@ -50,7 +50,7 @@ export async function clean() {
50
50
  logError("Failed to clean npm cache, skipping");
51
51
  }
52
52
 
53
- // 4. 清理 npx 缓存
53
+ // 4. Clean npx cache
54
54
  const npxCache = join(home, ".npm", "_npx");
55
55
  if (existsSync(npxCache)) {
56
56
  rmSync(npxCache, { recursive: true, force: true });
@@ -1,10 +1,10 @@
1
1
  /**
2
- * create-card:通过 x402 协议在 BSC 上用 USDT 支付,发一张一次性虚拟卡
2
+ * create-card: issue a one-time virtual card by paying with USDT on BSC over the x402 protocol.
3
3
  *
4
- * 服务端路径:GET {serviceUrl}/open/ai/x402/card/create?amount=<usd>&appId=<merchant>
5
- * 流程:fetch payment requirements balance + allowance 检查
6
- * (余额不足时)走 funding.mjs/fundSessionKey 充值
7
- * x402 EIP-712 签名提交 可选轮询 status
4
+ * Server endpoint: GET {serviceUrl}/open/ai/x402/card/create?amount=<usd>&appId=<merchant>
5
+ * Flow: fetch payment requirements -> check balance + allowance
6
+ * -> (if balance is insufficient) top up via funding.mjs/fundSessionKey
7
+ * -> submit x402 EIP-712 signature -> optionally poll status
8
8
  */
9
9
  import { createX402Api, decodePaymentResponse, fetchPaymentRequirements } from "../x402.mjs";
10
10
  import { resolve } from "../config.mjs";
@@ -82,11 +82,13 @@ export async function createCard(opts) {
82
82
  let sessionAddress;
83
83
  let topupAmount = null;
84
84
  let balanceInitialUsdt = null;
85
+ let balanceBeforeChargeUsdt = null;
85
86
 
86
87
  try {
87
88
  const { address, usdt, bnb, bnbRaw } = await getWalletBalance(privateKey);
88
89
  sessionAddress = address;
89
90
  balanceInitialUsdt = usdt;
91
+ balanceBeforeChargeUsdt = usdt;
90
92
  const usdtNum = parseFloat(usdt);
91
93
  logInfo(`Wallet: ${address}`);
92
94
  logInfo(`Balance: ${usdt} USDT, ${bnb} BNB`);
@@ -161,7 +163,7 @@ export async function createCard(opts) {
161
163
  return;
162
164
  }
163
165
 
164
- // Dry-run:跑完前置检查就退出
166
+ // Dry-run: exit after preflight checks complete
165
167
  if (dryRun) {
166
168
  const preview = {
167
169
  dryRun: true,
@@ -189,7 +191,7 @@ export async function createCard(opts) {
189
191
  return;
190
192
  }
191
193
 
192
- // WalletConnect 充值
194
+ // WalletConnect top-up
193
195
  if (needTopup || needGas) {
194
196
  logInfo("Funding flow triggered...");
195
197
  try {
@@ -210,6 +212,7 @@ export async function createCard(opts) {
210
212
  logInfo("Re-checking wallet balance...");
211
213
  try {
212
214
  const { usdt, bnbRaw } = await getWalletBalance(privateKey);
215
+ balanceBeforeChargeUsdt = usdt;
213
216
  const usdtNum = parseFloat(usdt);
214
217
  if (needGas && bnbRaw === 0n) {
215
218
  emitErr("create-card", "INSUFFICIENT_BNB", {
@@ -260,12 +263,28 @@ export async function createCard(opts) {
260
263
  const paymentResponse = decodePaymentResponse(response.headers);
261
264
  const orderNo = paymentReq.orderNo || response.data?.model?.orderNo || response.data?.orderNo;
262
265
 
266
+ let balanceAfterUsdt = null;
267
+ try {
268
+ const after = await getWalletBalance(privateKey);
269
+ balanceAfterUsdt = after.usdt;
270
+ } catch (e) {
271
+ logInfo(`Post-payment balance check failed: ${e.message}`);
272
+ }
273
+
263
274
  const sanitizedData = sanitizeOutput(response.data);
264
275
  const successData = {
265
276
  appId,
266
277
  orderNo,
278
+ amount,
267
279
  data: sanitizedData,
268
280
  paymentResponse,
281
+ balance: {
282
+ initial: balanceInitialUsdt,
283
+ before: balanceBeforeChargeUsdt,
284
+ after: balanceAfterUsdt,
285
+ charged: requiredUsdt,
286
+ topup: topupAmount,
287
+ },
269
288
  };
270
289
 
271
290
  function findCardStatus(obj) {
@@ -1,10 +1,10 @@
1
1
  /**
2
- * create-image:通过 x402 调用 Skill Boss 生成 AI 图像
2
+ * create-image: generate an AI image via Skill Boss using the x402 protocol.
3
3
  *
4
- * 服务端路径:GET {serviceUrl}/open/ai/x402/skillBoss/create?body=<urlencoded-json>&appId=<merchant>
5
- * 流程:fetch payment requirements balance + allowance 检查
6
- * (余额不足时)走 funding.mjs/fundSessionKey 充值
7
- * x402 EIP-712 签名提交 下载图片到本地
4
+ * Server endpoint: GET {serviceUrl}/open/ai/x402/skillBoss/create?body=<urlencoded-json>&appId=<merchant>
5
+ * Flow: fetch payment requirements -> check balance + allowance
6
+ * -> (if balance is insufficient) top up via funding.mjs/fundSessionKey
7
+ * -> submit x402 EIP-712 signature -> download the image locally
8
8
  */
9
9
  import { createX402Api, decodePaymentResponse, fetchPaymentRequirements } from "../x402.mjs";
10
10
  import { resolve } from "../config.mjs";
@@ -1,5 +1,6 @@
1
1
  /**
2
- * gas 命令:通过 WalletConnect 从主钱包向本地钱包转 BNB(withdraw 时支付 gas)
2
+ * gas command: transfer BNB from the main wallet to the local wallet via WalletConnect
3
+ * (used to pay gas during withdraw).
3
4
  */
4
5
  import { loadConfig } from "../config.mjs";
5
6
  import { getBalanceByAddress } from "../balance.mjs";
@@ -1,16 +1,16 @@
1
1
  /**
2
- * wallet-init:本地 session 钱包 check / 创建 + 链上状态评估
2
+ * wallet-init: check / create the local session wallet and assess its on-chain status.
3
3
  *
4
- * 步骤:
5
- * 1. ~/.aigateway/config.json privateKey → viem.generatePrivateKey() 生成
6
- * 2. USDT / BNB 余额(除非刚 created,则跳过查询直接判定为 needsTopup)
7
- * 3. facilitator allowance(同上规则)
8
- * 4. 综合判定 needsTopup 与原因,返回完整就绪状态供 agent 决策
4
+ * Steps:
5
+ * 1. If ~/.aigateway/config.json has no privateKey → generate one with viem.generatePrivateKey().
6
+ * 2. Query USDT / BNB balance (skipped when the wallet was just created — it must be empty).
7
+ * 3. Query the facilitator allowance (same rule).
8
+ * 4. Decide needsTopup with the reason and return the full readiness state for the agent to act on.
9
9
  *
10
- * 设计意图:agent 跑完 wallet-init 一条命令就拿到决策依据:
11
- * - data.ready=true 表示钱包私钥可用
12
- * - data.needsTopup=true → 必须先 wallet-topupenvelope 里附带 presets / minTopup / reason
13
- * - data.needsTopup=false → 可直接 create-card / create-image
10
+ * Design intent: with a single wallet-init call, the agent gets every decision input it needs:
11
+ * - data.ready=true → the session private key is usable
12
+ * - data.needsTopup=true → wallet-topup must run first (the envelope includes presets / minTopup / reason)
13
+ * - data.needsTopup=false → can proceed directly to create-card / create-image
14
14
  */
15
15
  import { loadConfig, saveConfig } from "../config.mjs";
16
16
  import { MIN_AMOUNT, MAX_AMOUNT } from "../constants.mjs";
@@ -41,7 +41,7 @@ export async function initWallet(opts) {
41
41
  logInfo(`Wallet: ${config.address}`);
42
42
  }
43
43
 
44
- // 链上状态评估
44
+ // On-chain status check
45
45
  let usdt = "0";
46
46
  let bnb = "0";
47
47
  let usdtNum = 0;
@@ -50,7 +50,7 @@ export async function initWallet(opts) {
50
50
  let chainCheckError = null;
51
51
 
52
52
  if (created) {
53
- // 刚建好的钱包余额必然为 0,跳过链上查询节省 ~500ms
53
+ // A freshly created wallet is guaranteed to be empty; skip the chain query to save ~500ms.
54
54
  logInfo("Fresh wallet — skipping balance lookup (assumed empty).");
55
55
  } else {
56
56
  try {
@@ -68,18 +68,19 @@ export async function initWallet(opts) {
68
68
  }
69
69
  }
70
70
 
71
- // 决策:是否需要 topup —— 只看链上真实状态,不依赖 config.mainWallet 字段。
72
- // 之前用 !config.mainWallet 当判断条件是错的:mainWallet 只是 withdraw 默认目标地址,
73
- // 即使为 null(如用户外部转账 / 旧版本未记录),只要链上 USDT/allowance 充足就应该
74
- // 直接允许付费调用,不要强制再走一次 wallet-topup。
71
+ // Decision: needsTopup. Use only real on-chain state — do NOT depend on config.mainWallet.
72
+ // The previous logic `created || !config.mainWallet` was wrong: mainWallet is purely the default
73
+ // destination for withdraw. If USDT / allowance on-chain are sufficient — even when mainWallet
74
+ // is null (external CEX deposit / older versions that didn't record it) — paid calls should be
75
+ // allowed without forcing another wallet-topup round.
75
76
  let needsTopup = false;
76
77
  let topupReason = null;
77
78
  if (created) {
78
- // 刚生成的 session key 必然没钱,无需查链
79
+ // A freshly generated session key has no funds — no point querying the chain.
79
80
  needsTopup = true;
80
81
  topupReason = "first_time";
81
82
  } else if (!chainCheckOk) {
82
- // 链上查询失败 —— 保守标记需要 topup 由用户决定下一步
83
+ // Chain probe failed conservatively flag needsTopup so the user can decide what to do.
83
84
  needsTopup = true;
84
85
  topupReason = "chain_check_failed";
85
86
  } else if (usdtNum < LOW_BALANCE_THRESHOLD) {
@@ -102,7 +103,7 @@ export async function initWallet(opts) {
102
103
  bnb,
103
104
  allowance: allowance.toString(),
104
105
  needsTopup,
105
- topupReason, // "no_prior_funding" | "low_balance" | "no_approve" | null
106
+ topupReason, // "first_time" | "low_balance" | "no_approve" | "chain_check_failed" | null
106
107
  minTopup: MIN_TOPUP_USDT,
107
108
  presets: TOPUP_PRESETS,
108
109
  amountLimits: { min: MIN_AMOUNT, max: MAX_AMOUNT },
@@ -1,15 +1,15 @@
1
1
  /**
2
- * topup:充值 + 一次性 approve facilitator
2
+ * wallet-topup: top up the session wallet and run the one-time facilitator approve.
3
3
  *
4
- * 流程:
5
- * 1. 校验 session key 已存在(来自 aigateway wallet-init
6
- * 2. 检查 USDT 余额 + facilitator allowance
7
- * 3. 如果 USDT < LOW_BALANCE_THRESHOLD1 USDT)或 allowance == 0,触发 WalletConnect 充值
8
- * - 充值 amount: TTY 模式交互选择 presets [5, 10, 20, 50] 或自定义;
9
- *TTY 模式需要传 --amount <usdt>,否则报 TOPUP_REQUIRED
10
- * - 同时按需转 0.0003 BNB 用作 approve gas
11
- * 4. session key 自己广播 ERC20.approve(facilitator, MaxUint256)
12
- * 5. 重新查余额 / allowance,返回最终状态
4
+ * Flow:
5
+ * 1. Verify the session key exists (created earlier by `aigateway wallet-init`).
6
+ * 2. Check USDT balance + facilitator allowance.
7
+ * 3. If USDT < LOW_BALANCE_THRESHOLD (1 USDT) or allowance == 0, open WalletConnect to fund:
8
+ * - Top-up amount: TTY mode picks interactively from presets [5, 10, 20, 50] or a custom value;
9
+ * non-TTY mode requires `--amount <usdt>`, otherwise TOPUP_REQUIRED is emitted.
10
+ * - 0.0003 BNB is transferred too when an approve transaction is needed.
11
+ * 4. The session key broadcasts ERC20.approve(facilitator, MaxUint256) once.
12
+ * 5. Re-query balance / allowance and return the final state.
13
13
  */
14
14
  import { resolve } from "../config.mjs";
15
15
  import { getWalletBalance, getAllowance } from "../balance.mjs";
@@ -71,7 +71,7 @@ export async function topup(opts) {
71
71
  const needApprove = allowance === 0n;
72
72
  const needGas = needApprove && bnbRaw === 0n;
73
73
 
74
- // 已就绪:余额够 + approve
74
+ // Already prepared: balance is sufficient and facilitator is approved
75
75
  if (!needTopup && !needApprove) {
76
76
  logInfo("Wallet already prepared (balance ≥ minimum, facilitator approved).");
77
77
  const data = {
@@ -89,7 +89,7 @@ export async function topup(opts) {
89
89
  return;
90
90
  }
91
91
 
92
- // 决定充值金额
92
+ // Decide the top-up amount
93
93
  let topupAmount = null;
94
94
  if (needTopup) {
95
95
  if (balanceLow) {
@@ -134,7 +134,7 @@ export async function topup(opts) {
134
134
  }
135
135
  }
136
136
 
137
- // WalletConnect 充值(USDT + 按需 BNB gas
137
+ // WalletConnect top-up (USDT + optional BNB for approve gas)
138
138
  if (needTopup || needGas) {
139
139
  const willTransfer = [];
140
140
  if (needTopup) willTransfer.push(`${topupAmount} USDT`);
@@ -161,7 +161,7 @@ export async function topup(opts) {
161
161
  }
162
162
  }
163
163
 
164
- // session key 一次性 approve facilitator
164
+ // Session key broadcasts the one-time facilitator approve
165
165
  let approveTx = null;
166
166
  if (needApprove) {
167
167
  let postBnbRaw = bnbRaw;
@@ -193,7 +193,7 @@ export async function topup(opts) {
193
193
  }
194
194
  }
195
195
 
196
- // 最终查余额 + allowance
196
+ // Final balance + allowance re-check
197
197
  let finalUsdt = usdt;
198
198
  let finalBnb = bnb;
199
199
  let finalAllowance = allowance;