@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 +46 -8
- package/bin/cli.mjs +3 -2
- package/package.json +1 -1
- package/scripts/postinstall.mjs +6 -6
- package/skills/aigateway/SKILL.md +65 -27
- package/src/balance.mjs +7 -7
- package/src/commands/clean.mjs +5 -5
- package/src/commands/create-card.mjs +26 -7
- package/src/commands/create-image.mjs +5 -5
- package/src/commands/wallet-gas.mjs +2 -1
- package/src/commands/wallet-init.mjs +20 -19
- package/src/commands/wallet-topup.mjs +15 -15
- package/src/commands/wallet-withdraw.mjs +7 -7
- package/src/config.mjs +6 -5
- package/src/error-codes.mjs +11 -11
- package/src/output.mjs +19 -16
- package/src/sanitize.mjs +11 -11
- package/src/update-check.mjs +11 -11
- package/src/walletconnect.mjs +65 -63
- package/src/x402.mjs +13 -10
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`
|
|
28
|
-
|
|
29
|
-
**into your session wallet**"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
11
|
-
//
|
|
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
|
+
"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": {
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* npm install -g
|
|
4
|
+
* After `npm install -g`, automatically install the skill into every detected AI coding tool.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
//
|
|
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
|
|
33
|
+
// skills CLI unavailable or failed — fall back
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// Fallback:
|
|
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.
|
|
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
|
|
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 —
|
|
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,
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
|
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
|
|
423
|
-
| --- | --- | --- | --- |
|
|
424
|
-
| Step 2 (`wallet-topup`) | USDT into the **session wallet** | "load USDT **into your session wallet**" |
|
|
425
|
-
| Step 3a (`create-card`) | USD **face value** loaded onto a new card | "card **face value**" / "issue a card with how much" |
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
81
|
-
* @param {string} ownerAddress - session key
|
|
82
|
-
* @returns {bigint}
|
|
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();
|
package/src/commands/clean.mjs
CHANGED
|
@@ -8,7 +8,7 @@ export async function clean() {
|
|
|
8
8
|
const home = homedir();
|
|
9
9
|
const removed = [];
|
|
10
10
|
|
|
11
|
-
// 1.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
2
|
+
* create-card: issue a one-time virtual card by paying with USDT on BSC over the x402 protocol.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
|
2
|
+
* create-image: generate an AI image via Skill Boss using the x402 protocol.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
|
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
|
|
2
|
+
* wallet-init: check / create the local session wallet and assess its on-chain status.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 1.
|
|
6
|
-
* 2.
|
|
7
|
-
* 3.
|
|
8
|
-
* 4.
|
|
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
|
-
*
|
|
11
|
-
* - data.ready=true
|
|
12
|
-
* - data.needsTopup=true →
|
|
13
|
-
* - data.needsTopup=false →
|
|
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
|
-
//
|
|
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
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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, // "
|
|
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
|
|
2
|
+
* wallet-topup: top up the session wallet and run the one-time facilitator approve.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 1.
|
|
6
|
-
* 2.
|
|
7
|
-
* 3.
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* 4. session key
|
|
12
|
-
* 5.
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
196
|
+
// Final balance + allowance re-check
|
|
197
197
|
let finalUsdt = usdt;
|
|
198
198
|
let finalBnb = bnb;
|
|
199
199
|
let finalAllowance = allowance;
|