@agenttech/tpay-cli 0.0.1 → 0.0.3
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/README.md +145 -62
- package/dist/index.js +79 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,87 +1,170 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @agenttech/tpay-cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Send USDT payments on Solana via the T402 x402 v2 protocol. Designed for AI agents — all output is structured JSON on stdout, logs go to stderr.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @agenttech/tpay-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Verify:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
tpay version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
The CLI needs a BIP-39 seed phrase to sign transactions. Two setup paths:
|
|
20
|
+
|
|
21
|
+
### Non-interactive (recommended for agents)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
WALLET_SEED_PHRASE="your twelve or twenty four word seed phrase here" \
|
|
25
|
+
TPAY_PASSPHRASE="encryption-password" \
|
|
26
|
+
tpay setup
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This encrypts and saves the seed phrase to `~/.config/tpay/.env`.
|
|
30
|
+
|
|
31
|
+
### From env file
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
tpay setup --from-env /path/to/.env
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The file must contain `WALLET_SEED_PHRASE=...`.
|
|
38
|
+
|
|
39
|
+
### Skip setup (inline seed phrase)
|
|
40
|
+
|
|
41
|
+
You can skip `tpay setup` entirely by passing the seed phrase as an environment variable on every call:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
WALLET_SEED_PHRASE="your seed phrase" tpay send --to <address> --amount 10
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Setup output
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{"status":"success","saved":true}
|
|
51
|
+
```
|
|
5
52
|
|
|
6
53
|
## Commands
|
|
7
54
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
| `tpay setup --from-env <file>` | Import keys from an env file |
|
|
12
|
-
| `tpay send --to <addr> --amount <n> --chain <chain>` | Send payment |
|
|
13
|
-
| `tpay intent status <intent_id>` | Check payment status |
|
|
14
|
-
| `tpay version` | Print version |
|
|
15
|
-
| `tpay help` / `tpay --help` | Show all commands |
|
|
16
|
-
| `tpay send --help` | Show send args |
|
|
17
|
-
|
|
18
|
-
Global flags:
|
|
19
|
-
- `--verbose` — debug output to stderr
|
|
20
|
-
- `--format text|json` — output format (default: `json`)
|
|
55
|
+
### `tpay send`
|
|
56
|
+
|
|
57
|
+
Send a USDT payment.
|
|
21
58
|
|
|
22
59
|
```bash
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{"status":"success","intent_id":"...","tx_hash":"...","explorer_url":"..."}
|
|
60
|
+
tpay send --to <solana-address> --amount <number>
|
|
61
|
+
```
|
|
26
62
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
tx_hash: 0x...
|
|
32
|
-
explorer_url: https://...
|
|
63
|
+
Or pipe JSON via stdin:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
echo '{"to":"<solana-address>","amount":"10"}' | tpay send
|
|
33
67
|
```
|
|
34
68
|
|
|
35
|
-
|
|
69
|
+
**Success output:**
|
|
36
70
|
|
|
37
|
-
|
|
71
|
+
```json
|
|
72
|
+
{"status":"success","intent_id":"intent_abc123","tx_hash":"5xY...","explorer_url":"https://..."}
|
|
73
|
+
```
|
|
38
74
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
| `solana` | Solana Mainnet |
|
|
45
|
-
| `solana-devnet` | Solana Devnet |
|
|
75
|
+
**Error output:**
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{"status":"error","error_type":"payment_error","message":"Payment failed","intent_id":"intent_abc123"}
|
|
79
|
+
```
|
|
46
80
|
|
|
47
|
-
|
|
81
|
+
### `tpay balance`
|
|
48
82
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
83
|
+
Check SOL and USDT balances for any address. No wallet keys required.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
tpay balance --address <solana-address>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Output:**
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{"status":"ok","address":"<solana-address>","sol":"1.5","usdt":"100.0"}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `tpay intent status`
|
|
54
96
|
|
|
55
|
-
|
|
97
|
+
Check the status of a payment intent.
|
|
56
98
|
|
|
57
99
|
```bash
|
|
58
|
-
|
|
59
|
-
SOLANA_RPC_URL=https://your-rpc.example.com \
|
|
60
|
-
SOLANA_FEE_PAYER=<base58> \
|
|
61
|
-
bun build --compile src/index.ts --outfile tpay
|
|
100
|
+
tpay intent status <intent_id>
|
|
62
101
|
```
|
|
63
102
|
|
|
64
|
-
|
|
103
|
+
Or via stdin:
|
|
65
104
|
|
|
66
105
|
```bash
|
|
67
|
-
|
|
68
|
-
bun test
|
|
69
|
-
T402_API_URL=https://... WALLET_EVM_PRIVATE_KEY=0x... bun run src/index.ts send --to 0x... --amount 1 --chain base
|
|
106
|
+
echo '{"intent_id":"intent_abc123"}' | tpay intent status
|
|
70
107
|
```
|
|
71
108
|
|
|
72
|
-
|
|
109
|
+
**Output:**
|
|
73
110
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
111
|
+
```json
|
|
112
|
+
{"status":"ok","intent_id":"intent_abc123","payment_status":"BASE_SETTLED","sending_amount":"10","receiving_amount":"10","payer_chain":"solana","created_at":"...","expires_at":"...","tx_hash":"5xY...","explorer_url":"https://..."}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `tpay version`
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
tpay version
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `tpay help`
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
tpay help
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Global Flags
|
|
128
|
+
|
|
129
|
+
| Flag | Description |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `--verbose` | Debug logs to stderr |
|
|
132
|
+
| `--format json\|text` | Output format (default: `json`) |
|
|
133
|
+
|
|
134
|
+
## Environment Variables
|
|
135
|
+
|
|
136
|
+
| Variable | Required | Description |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `WALLET_SEED_PHRASE` | Yes (if no config file) | BIP-39 mnemonic (12–24 words) |
|
|
139
|
+
| `TPAY_PASSPHRASE` | No | Decrypts saved config at `~/.config/tpay/.env` |
|
|
140
|
+
| `SOLANA_FEE_PAYER` | No | Override fee payer address |
|
|
141
|
+
|
|
142
|
+
If both `WALLET_SEED_PHRASE` env var and an encrypted config file exist, the env var takes precedence.
|
|
143
|
+
|
|
144
|
+
## Exit Codes
|
|
145
|
+
|
|
146
|
+
| Code | Type | Meaning |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| 0 | — | Success |
|
|
149
|
+
| 1 | `validation_error` | Invalid arguments or input |
|
|
150
|
+
| 2 | `runtime_error` | Unexpected runtime failure |
|
|
151
|
+
| 3 | `configuration_error` | Missing or invalid config |
|
|
152
|
+
| 4 | `network_error` | API or RPC failure |
|
|
153
|
+
| 5 | `payment_error` | Payment-specific failure |
|
|
154
|
+
|
|
155
|
+
All errors output JSON to stdout:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{"status":"error","error_type":"<type>","message":"<description>"}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Supported Chains
|
|
162
|
+
|
|
163
|
+
| Chain | Network ID |
|
|
164
|
+
|---|---|
|
|
165
|
+
| Solana Mainnet | `solana` |
|
|
166
|
+
| Solana Devnet | `solana-devnet` |
|
|
79
167
|
|
|
80
|
-
##
|
|
168
|
+
## License
|
|
81
169
|
|
|
82
|
-
|
|
83
|
-
- [ ] Implement `WalletPlugin` interface (`name`, `getEvmPrivateKey()`, `getSolanaSeed()`)
|
|
84
|
-
- [ ] Add to `WALLET_PLUGINS` map in `src/loader.ts`
|
|
85
|
-
- [ ] Document required env vars above
|
|
86
|
-
- [ ] Ensure key/seed return values are never logged
|
|
87
|
-
- [ ] Test: verify loads when `WALLET_PROVIDER=<name>` is set
|
|
170
|
+
ISC
|
package/dist/index.js
CHANGED
|
@@ -64009,6 +64009,7 @@ async function runHelp(ctx) {
|
|
|
64009
64009
|
commands: {
|
|
64010
64010
|
setup: 'Configure wallet keys with encryption. Use --from-env <file> to import. Non-interactive: WALLET_SEED_PHRASE="..." TPAY_PASSPHRASE="..." tpay setup',
|
|
64011
64011
|
send: "Send USDC/USDT via T402. Args: --to, --amount",
|
|
64012
|
+
balance: "Show wallet SOL and USDT balances",
|
|
64012
64013
|
"intent status <intent_id>": "Fetch current status of a payment intent",
|
|
64013
64014
|
version: "Print CLI version",
|
|
64014
64015
|
help: "Show this help"
|
|
@@ -64036,7 +64037,7 @@ async function runHelp(ctx) {
|
|
|
64036
64037
|
async function runVersion(ctx) {
|
|
64037
64038
|
output(ctx.format, {
|
|
64038
64039
|
name: "@agenttech/tpay-cli",
|
|
64039
|
-
version: "0.0.
|
|
64040
|
+
version: "0.0.3"
|
|
64040
64041
|
});
|
|
64041
64042
|
return 0;
|
|
64042
64043
|
}
|
|
@@ -64236,7 +64237,6 @@ var CONFIG = {
|
|
|
64236
64237
|
feePayer: ""
|
|
64237
64238
|
}
|
|
64238
64239
|
};
|
|
64239
|
-
console.log("CONFIG", CONFIG);
|
|
64240
64240
|
|
|
64241
64241
|
class SendCommand extends BaseCommand {
|
|
64242
64242
|
async execute(opts) {
|
|
@@ -64344,12 +64344,85 @@ async function runIntentStatus(ctx, intentId) {
|
|
|
64344
64344
|
return command.execute(intentId);
|
|
64345
64345
|
}
|
|
64346
64346
|
|
|
64347
|
+
// src/cli/commands/balance.ts
|
|
64348
|
+
init_index_cjs();
|
|
64349
|
+
var CONFIG3 = {
|
|
64350
|
+
apiUrl: "https://api-staging-t402-pay.agent.tech",
|
|
64351
|
+
solana: {
|
|
64352
|
+
rpcUrl: "https://api.mainnet-beta.solana.com",
|
|
64353
|
+
feePayer: ""
|
|
64354
|
+
}
|
|
64355
|
+
};
|
|
64356
|
+
var USDT_MINT = new $PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");
|
|
64357
|
+
var TOKEN_PROGRAM_ID2 = new $PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
64358
|
+
var LAMPORTS_PER_SOL = 1000000000n;
|
|
64359
|
+
var USDT_DECIMALS = 6;
|
|
64360
|
+
|
|
64361
|
+
class BalanceCommand extends BaseCommand {
|
|
64362
|
+
async execute(opts) {
|
|
64363
|
+
const address3 = this.requireArg(opts.address, "address");
|
|
64364
|
+
let pubkey;
|
|
64365
|
+
try {
|
|
64366
|
+
pubkey = new $PublicKey(address3);
|
|
64367
|
+
} catch {
|
|
64368
|
+
throw new ValidationError(`Invalid Solana address: ${address3}`);
|
|
64369
|
+
}
|
|
64370
|
+
const connection = new $Connection(CONFIG3.solana.rpcUrl);
|
|
64371
|
+
try {
|
|
64372
|
+
this.ctx.logger.debug("Fetching balances", { address: address3 });
|
|
64373
|
+
const [solLamports, tokenAccounts] = await Promise.all([
|
|
64374
|
+
connection.getBalance(pubkey),
|
|
64375
|
+
connection.getParsedTokenAccountsByOwner(pubkey, { mint: USDT_MINT, programId: TOKEN_PROGRAM_ID2 })
|
|
64376
|
+
]);
|
|
64377
|
+
let usdtRaw = 0n;
|
|
64378
|
+
for (const { account } of tokenAccounts.value) {
|
|
64379
|
+
const amount = account.data.parsed?.info?.tokenAmount?.amount;
|
|
64380
|
+
if (amount)
|
|
64381
|
+
usdtRaw += BigInt(amount);
|
|
64382
|
+
}
|
|
64383
|
+
output(this.ctx.format, {
|
|
64384
|
+
status: "ok",
|
|
64385
|
+
address: address3,
|
|
64386
|
+
sol: formatLamports(BigInt(solLamports)),
|
|
64387
|
+
usdt: formatToken(usdtRaw, USDT_DECIMALS)
|
|
64388
|
+
});
|
|
64389
|
+
return 0;
|
|
64390
|
+
} catch (error) {
|
|
64391
|
+
if (error instanceof ValidationError)
|
|
64392
|
+
throw error;
|
|
64393
|
+
if (error instanceof Error && error.message.includes("Server error")) {
|
|
64394
|
+
throw new NetworkError(error.message, error);
|
|
64395
|
+
}
|
|
64396
|
+
throw error;
|
|
64397
|
+
}
|
|
64398
|
+
}
|
|
64399
|
+
}
|
|
64400
|
+
function formatLamports(lamports) {
|
|
64401
|
+
const whole = lamports / LAMPORTS_PER_SOL;
|
|
64402
|
+
const frac = lamports % LAMPORTS_PER_SOL;
|
|
64403
|
+
if (frac === 0n)
|
|
64404
|
+
return whole.toString();
|
|
64405
|
+
return `${whole}.${frac.toString().padStart(9, "0").replace(/0+$/, "")}`;
|
|
64406
|
+
}
|
|
64407
|
+
function formatToken(raw, decimals) {
|
|
64408
|
+
const divisor = 10n ** BigInt(decimals);
|
|
64409
|
+
const whole = raw / divisor;
|
|
64410
|
+
const frac = raw % divisor;
|
|
64411
|
+
if (frac === 0n)
|
|
64412
|
+
return whole.toString();
|
|
64413
|
+
return `${whole}.${frac.toString().padStart(decimals, "0").replace(/0+$/, "")}`;
|
|
64414
|
+
}
|
|
64415
|
+
async function runBalance(ctx, opts) {
|
|
64416
|
+
const command = new BalanceCommand(ctx);
|
|
64417
|
+
return command.execute(opts);
|
|
64418
|
+
}
|
|
64419
|
+
|
|
64347
64420
|
// src/cli/program.ts
|
|
64348
64421
|
function createProgram() {
|
|
64349
64422
|
const program2 = new Command;
|
|
64350
64423
|
const versionInfo = {
|
|
64351
64424
|
name: "@agenttech/tpay-cli",
|
|
64352
|
-
version: "0.0.
|
|
64425
|
+
version: "0.0.3"
|
|
64353
64426
|
};
|
|
64354
64427
|
const versionString = typeof versionInfo === "string" ? versionInfo : versionInfo.version;
|
|
64355
64428
|
program2.name("tpay").version(versionString, "-v, --version", "Show version").option("--verbose", "Enable debug logging to stderr").option("--format <fmt>", "Output format: json | text", "json");
|
|
@@ -64389,6 +64462,9 @@ function registerCommands(program2) {
|
|
|
64389
64462
|
await loadEnv();
|
|
64390
64463
|
process.exitCode = await runSend(getContext(), opts);
|
|
64391
64464
|
});
|
|
64465
|
+
program2.command("balance").description("Show wallet SOL and USDT balances").option("--address <addr>", "Solana wallet address to query").action(async (opts) => {
|
|
64466
|
+
process.exitCode = await runBalance(getContext(), opts);
|
|
64467
|
+
});
|
|
64392
64468
|
const intentCmd = program2.command("intent").description("Payment intent operations");
|
|
64393
64469
|
intentCmd.command("status [intentId]").description("Check payment intent status").action(async (intentId) => {
|
|
64394
64470
|
process.exitCode = await runIntentStatus(getContext(), intentId);
|