thaifi-wallet-cli 0.1.3 → 0.2.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.
- package/README.md +10 -5
- package/dist/chain.js +5 -5
- package/dist/commands/balance.js +54 -35
- package/dist/commands/fund.js +2 -2
- package/dist/commands/login.js +1 -1
- package/dist/commands/tokens.js +9 -0
- package/dist/commands/transfer.js +30 -10
- package/dist/commands/whoami.js +23 -10
- package/dist/index.js +9 -4
- package/dist/tokens.js +39 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,13 +16,14 @@ wallet.thaifi.com/pair?id=…&code=… ← user approves (passkey / PIN)
|
|
|
16
16
|
│ 2. web sends authorizeKey(keyId, P256, expiry, limits) on-chain
|
|
17
17
|
▼
|
|
18
18
|
access key authorized on-chain → the CLI signs transactions directly,
|
|
19
|
-
spending up to the limit
|
|
19
|
+
spending up to the limit per token, fees (gas) paid in pathUSD.
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
- Non-custodial: the private key never leaves the machine; the web wallet
|
|
23
23
|
never sees it.
|
|
24
24
|
- One wallet per account: revoking an app on the web cuts access immediately.
|
|
25
|
-
- Limits are on-chain: `SpendingLimitExceeded` when exceeded; keys expire.
|
|
25
|
+
- Limits are on-chain, per token: `SpendingLimitExceeded` when exceeded; keys expire.
|
|
26
|
+
- Supported tokens (TIP-20, 6 decimals): **pathUSD** (gas), **THCFI**, **THCOC**.
|
|
26
27
|
|
|
27
28
|
## Install / run
|
|
28
29
|
|
|
@@ -39,13 +40,17 @@ Set `THAIFI_WALLET_URL` / `THAIFI_RPC_URL` to target other deployments.
|
|
|
39
40
|
| Command | Description |
|
|
40
41
|
|---|---|
|
|
41
42
|
| `login [--no-browser]` | Pair with the web wallet (approval URL for headless machines) |
|
|
42
|
-
| `whoami` | Account, agent key,
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
43
|
+
| `whoami` | Account, agent key, balances, remaining spend limits |
|
|
44
|
+
| `tokens` | List supported TIP-20 tokens (symbol, decimals, address) |
|
|
45
|
+
| `balance [--token <symbol\|addr>]` | Balances (default: all supported tokens) |
|
|
46
|
+
| `transfer <to> <amount> [--token <symbol\|addr>]` | Send tokens (default: pathUSD; limit enforced on-chain) |
|
|
45
47
|
| `request <url> [-X -H -d --json --dry-run --max-spend]` | HTTP with automatic x402/MPP payment |
|
|
46
48
|
| `services [--search q]` | Discover services from the MPP registry (`THAIFI_MPP_REGISTRY`) |
|
|
47
49
|
| `logout` | Remove the local key (revoke on-chain from the web wallet) |
|
|
48
50
|
|
|
51
|
+
`--token` accepts a symbol (case-insensitive) or a contract address; unknown
|
|
52
|
+
addresses are treated as custom TIP-20s (6 decimals).
|
|
53
|
+
|
|
49
54
|
## Security notes
|
|
50
55
|
|
|
51
56
|
- The recovery password / passphrase is never involved here — the agent key is
|
package/dist/chain.js
CHANGED
|
@@ -30,9 +30,9 @@ export function createAgentClient(store) {
|
|
|
30
30
|
transport: http(store.rpcUrl),
|
|
31
31
|
}).extend(tempoActions());
|
|
32
32
|
}
|
|
33
|
-
export async function
|
|
33
|
+
export async function tokenBalance(client, token, address) {
|
|
34
34
|
return client.readContract({
|
|
35
|
-
address:
|
|
35
|
+
address: token,
|
|
36
36
|
abi: [
|
|
37
37
|
{
|
|
38
38
|
name: "balanceOf",
|
|
@@ -46,12 +46,12 @@ export async function pathUsdBalance(client, address) {
|
|
|
46
46
|
args: [address],
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
-
/** Remaining spend allowance of the agent key (
|
|
50
|
-
export async function remainingLimit(client, userAddress, keyId) {
|
|
49
|
+
/** Remaining spend allowance of the agent key (raw units of `token`). */
|
|
50
|
+
export async function remainingLimit(client, userAddress, keyId, token = CONFIG.pathUsd) {
|
|
51
51
|
const res = await client.accessKey.getRemainingLimit({
|
|
52
52
|
account: userAddress,
|
|
53
53
|
accessKey: keyId,
|
|
54
|
-
token
|
|
54
|
+
token,
|
|
55
55
|
});
|
|
56
56
|
return res.remaining;
|
|
57
57
|
}
|
package/dist/commands/balance.js
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
|
-
/** `thaifi balance [token]` — TIP-20
|
|
1
|
+
/** `thaifi balance [--token <symbol|address>]` — TIP-20 balances of the paired account. */
|
|
2
2
|
import { loadStore } from "../store.js";
|
|
3
3
|
import { createAgentClient } from "../chain.js";
|
|
4
|
-
import {
|
|
4
|
+
import { TOKENS, resolveToken } from "../tokens.js";
|
|
5
|
+
const BALANCE_ABI = [
|
|
6
|
+
{
|
|
7
|
+
name: "balanceOf",
|
|
8
|
+
type: "function",
|
|
9
|
+
stateMutability: "view",
|
|
10
|
+
inputs: [{ type: "address" }],
|
|
11
|
+
outputs: [{ type: "uint256" }],
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
const SYMBOL_ABI = [
|
|
15
|
+
{
|
|
16
|
+
name: "symbol",
|
|
17
|
+
type: "function",
|
|
18
|
+
stateMutability: "view",
|
|
19
|
+
inputs: [],
|
|
20
|
+
outputs: [{ type: "string" }],
|
|
21
|
+
},
|
|
22
|
+
];
|
|
5
23
|
export async function balance(options = {}) {
|
|
6
24
|
const store = loadStore();
|
|
7
25
|
if (!store?.userAddress) {
|
|
@@ -9,40 +27,41 @@ export async function balance(options = {}) {
|
|
|
9
27
|
process.exitCode = 1;
|
|
10
28
|
return;
|
|
11
29
|
}
|
|
12
|
-
|
|
30
|
+
let tokens;
|
|
31
|
+
if (options.token) {
|
|
32
|
+
try {
|
|
33
|
+
tokens = [resolveToken(options.token)];
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
tokens = TOKENS;
|
|
43
|
+
}
|
|
13
44
|
const client = createAgentClient(store);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
name: "balanceOf",
|
|
20
|
-
type: "function",
|
|
21
|
-
stateMutability: "view",
|
|
22
|
-
inputs: [{ type: "address" }],
|
|
23
|
-
outputs: [{ type: "uint256" }],
|
|
24
|
-
},
|
|
25
|
-
],
|
|
45
|
+
const userAddress = store.userAddress;
|
|
46
|
+
const rows = await Promise.all(tokens.map(async (t) => {
|
|
47
|
+
const raw = await client.readContract({
|
|
48
|
+
address: t.address,
|
|
49
|
+
abi: BALANCE_ABI,
|
|
26
50
|
functionName: "balanceOf",
|
|
27
|
-
args: [
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
43
|
-
.catch(() => "TOKEN"),
|
|
44
|
-
]);
|
|
45
|
-
const value = Number(balance) / 10 ** PATHUSD_DECIMALS;
|
|
46
|
-
console.log(symbol + " " + value.toLocaleString("en-US", { maximumFractionDigits: 6 }));
|
|
51
|
+
args: [userAddress],
|
|
52
|
+
});
|
|
53
|
+
// Registry tokens already know their symbol; custom ones read it from the chain.
|
|
54
|
+
const symbol = t.symbol === "TOKEN"
|
|
55
|
+
? await client
|
|
56
|
+
.readContract({ address: t.address, abi: SYMBOL_ABI, functionName: "symbol" })
|
|
57
|
+
.catch(() => "TOKEN")
|
|
58
|
+
: t.symbol;
|
|
59
|
+
const value = Number(raw) / 10 ** t.decimals;
|
|
60
|
+
return { symbol, value, token: t };
|
|
61
|
+
}));
|
|
62
|
+
const width = Math.max(...rows.map((r) => r.symbol.length));
|
|
63
|
+
for (const r of rows) {
|
|
64
|
+
console.log(r.symbol.padEnd(width) + " " + r.value.toLocaleString("en-US", { maximumFractionDigits: r.token.decimals }));
|
|
65
|
+
}
|
|
47
66
|
console.log("(" + store.userAddress + ")");
|
|
48
67
|
}
|
package/dist/commands/fund.js
CHANGED
|
@@ -19,8 +19,8 @@ export async function fund(options = {}) {
|
|
|
19
19
|
console.log("Deposit address (your wallet):");
|
|
20
20
|
console.log(" " + store.userAddress);
|
|
21
21
|
console.log("");
|
|
22
|
-
console.log("Send pathUSD (or any ThaiFi TIP-20 token) to this
|
|
23
|
-
console.log("from any ThaiFi wallet.");
|
|
22
|
+
console.log("Send pathUSD, THCFI or THCOC (or any ThaiFi TIP-20 token) to this");
|
|
23
|
+
console.log("address from any ThaiFi wallet. Keep some pathUSD — gas is paid in it.");
|
|
24
24
|
console.log("");
|
|
25
25
|
console.log("Deposit page: " + store.walletUrl + "/deposit");
|
|
26
26
|
console.log("(Bridge USDC from other chains — coming soon)");
|
package/dist/commands/login.js
CHANGED
|
@@ -93,7 +93,7 @@ export async function login(options = {}) {
|
|
|
93
93
|
console.log(" Wallet account: " + status.userAddress);
|
|
94
94
|
console.log(" Spend limit: " +
|
|
95
95
|
Number(status.limitAmount ?? 0) / 10 ** 6 +
|
|
96
|
-
"
|
|
96
|
+
" per token / " +
|
|
97
97
|
Math.round((status.limitPeriod ?? 0) / 86400) +
|
|
98
98
|
" days");
|
|
99
99
|
console.log(" Key expires: " + (status.expiry ? new Date(status.expiry * 1000).toISOString() : "never"));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** `thaifi tokens` — list TIP-20 tokens supported by the CLI. */
|
|
2
|
+
import { TOKENS } from "../tokens.js";
|
|
3
|
+
export async function tokens() {
|
|
4
|
+
const width = Math.max(...TOKENS.map((t) => t.symbol.length));
|
|
5
|
+
console.log("SYMBOL".padEnd(width) + " DECIMALS ADDRESS");
|
|
6
|
+
for (const t of TOKENS) {
|
|
7
|
+
console.log(t.symbol.padEnd(width) + " " + String(t.decimals).padEnd(8) + " " + t.address);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** `thaifi transfer <to> <amount> [token]` — TIP-20 transfer signed by the agent access key. */
|
|
2
|
-
import { parseUnits
|
|
1
|
+
/** `thaifi transfer <to> <amount> [--token <symbol|address>]` — TIP-20 transfer signed by the agent access key. */
|
|
2
|
+
import { parseUnits } from "viem";
|
|
3
3
|
import { loadStore } from "../store.js";
|
|
4
|
-
import { createAgentClient } from "../chain.js";
|
|
5
|
-
import {
|
|
4
|
+
import { createAgentClient, tokenBalance } from "../chain.js";
|
|
5
|
+
import { PATHUSD, resolveToken } from "../tokens.js";
|
|
6
6
|
export async function transfer(to, amount, options = {}) {
|
|
7
7
|
const store = loadStore();
|
|
8
8
|
if (!store?.userAddress) {
|
|
@@ -15,24 +15,44 @@ export async function transfer(to, amount, options = {}) {
|
|
|
15
15
|
process.exitCode = 1;
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
let token;
|
|
19
|
+
try {
|
|
20
|
+
token = resolveToken(options.token ?? PATHUSD.symbol);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
19
27
|
const client = createAgentClient(store);
|
|
28
|
+
const userAddress = store.userAddress;
|
|
29
|
+
const rawAmount = parseUnits(amount, token.decimals);
|
|
30
|
+
// Gas is always paid in pathUSD — a non-pathUSD transfer with an empty
|
|
31
|
+
// pathUSD balance can never land.
|
|
32
|
+
if (token.address.toLowerCase() !== PATHUSD.address.toLowerCase()) {
|
|
33
|
+
const gasBalance = await tokenBalance(client, PATHUSD.address, userAddress).catch(() => null);
|
|
34
|
+
if (gasBalance !== null && gasBalance <= 0n) {
|
|
35
|
+
console.error(`✗ pathUSD balance is 0 — gas is paid in pathUSD, so this ${token.symbol} transfer would fail. Receive some pathUSD first.`);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
console.log("Token: " + token.symbol + " (" + token.address + ")");
|
|
20
41
|
console.log("To: " + to);
|
|
21
|
-
console.log("Amount: " + amount + " (raw " +
|
|
42
|
+
console.log("Amount: " + amount + " (raw " + rawAmount + ")");
|
|
22
43
|
// Signed locally by the agent access key; the on-chain spending limit is
|
|
23
44
|
// enforced by the AccountKeychain precompile (SpendingLimitExceeded on overflow).
|
|
24
45
|
const hash = await client.token.transfer({
|
|
25
46
|
to: to,
|
|
26
|
-
amount:
|
|
27
|
-
token,
|
|
47
|
+
amount: rawAmount,
|
|
48
|
+
token: token.address,
|
|
28
49
|
});
|
|
29
50
|
console.log("Tx: " + hash);
|
|
30
51
|
console.log("Waiting for receipt…");
|
|
31
52
|
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
32
53
|
console.log(receipt.status === "success"
|
|
33
|
-
? "✓ Transferred " + amount + " (gas " + receipt.gasUsed + ")"
|
|
54
|
+
? "✓ Transferred " + amount + " " + token.symbol + " (gas " + receipt.gasUsed + ")"
|
|
34
55
|
: "✗ Transaction reverted (status " + receipt.status + ")");
|
|
35
56
|
if (receipt.status !== "success")
|
|
36
57
|
process.exitCode = 1;
|
|
37
|
-
void formatUnits;
|
|
38
58
|
}
|
package/dist/commands/whoami.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/** `thaifi whoami` — account, agent key, balances and remaining spend
|
|
1
|
+
/** `thaifi whoami` — account, agent key, balances and remaining spend limits. */
|
|
2
2
|
import { loadStore } from "../store.js";
|
|
3
|
-
import { createAgentClient,
|
|
4
|
-
|
|
3
|
+
import { createAgentClient, tokenBalance, remainingLimit } from "../chain.js";
|
|
4
|
+
import { TOKENS, PATHUSD } from "../tokens.js";
|
|
5
|
+
const fmt = (raw, decimals = 6) => (Number(raw) / 10 ** decimals).toLocaleString("en-US", { maximumFractionDigits: decimals });
|
|
5
6
|
export async function whoami() {
|
|
6
7
|
const store = loadStore();
|
|
7
8
|
if (!store?.userAddress) {
|
|
@@ -12,19 +13,31 @@ export async function whoami() {
|
|
|
12
13
|
const client = createAgentClient(store);
|
|
13
14
|
const userAddress = store.userAddress;
|
|
14
15
|
const keyId = store.keyId;
|
|
15
|
-
const [
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const [balances, remaining] = await Promise.all([
|
|
17
|
+
Promise.all(TOKENS.map((t) => tokenBalance(client, t.address, userAddress)
|
|
18
|
+
.then((raw) => [t.symbol, raw])
|
|
19
|
+
.catch(() => [t.symbol, null]))),
|
|
20
|
+
Promise.all(TOKENS.map((t) => remainingLimit(client, userAddress, keyId, t.address)
|
|
21
|
+
.then((raw) => [t.symbol, raw])
|
|
22
|
+
.catch(() => [t.symbol, null]))),
|
|
18
23
|
]);
|
|
19
24
|
console.log("Account: " + userAddress);
|
|
20
25
|
console.log("Agent key: " + keyId);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
const width = Math.max(...TOKENS.map((t) => t.symbol.length));
|
|
27
|
+
for (const [symbol, raw] of balances) {
|
|
28
|
+
const prefix = symbol === PATHUSD.symbol ? "$" : "";
|
|
29
|
+
console.log((symbol + ":").padEnd(width + 1) + " " + (raw === null ? "(read failed)" : prefix + fmt(raw)));
|
|
24
30
|
}
|
|
25
|
-
|
|
31
|
+
if (remaining.every(([, raw]) => raw === null)) {
|
|
26
32
|
console.log("Limit left: (unavailable — key may have been revoked)");
|
|
27
33
|
}
|
|
34
|
+
else {
|
|
35
|
+
const parts = remaining
|
|
36
|
+
.filter(([, raw]) => raw !== null)
|
|
37
|
+
.map(([symbol, raw]) => `${symbol} ${fmt(raw)}`)
|
|
38
|
+
.join(", ");
|
|
39
|
+
console.log("Limit left: " + parts + " (of " + Number(store.limitAmount ?? 0) / 10 ** 6 + " per token)");
|
|
40
|
+
}
|
|
28
41
|
console.log("Key expires: " + (store.keyExpiry ? new Date(store.keyExpiry * 1000).toISOString() : "never"));
|
|
29
42
|
console.log("Wallet: " + store.walletUrl);
|
|
30
43
|
}
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,12 @@ import { transfer } from "./commands/transfer.js";
|
|
|
9
9
|
import { request } from "./commands/request.js";
|
|
10
10
|
import { services } from "./commands/services.js";
|
|
11
11
|
import { fund } from "./commands/fund.js";
|
|
12
|
+
import { tokens } from "./commands/tokens.js";
|
|
12
13
|
const program = new Command();
|
|
13
14
|
program
|
|
14
15
|
.name("thaifi")
|
|
15
16
|
.description("ThaiFi Wallet CLI — pair with wallet.thaifi.com and sign with on-chain access keys")
|
|
16
|
-
.version("0.
|
|
17
|
+
.version("0.2.0");
|
|
17
18
|
program
|
|
18
19
|
.command("login")
|
|
19
20
|
.description("Pair this CLI with your ThaiFi Wallet (opens the browser to approve)")
|
|
@@ -27,17 +28,21 @@ program
|
|
|
27
28
|
.command("whoami")
|
|
28
29
|
.description("Show the paired account, agent key, balances and remaining spend limit")
|
|
29
30
|
.action(whoami);
|
|
31
|
+
program
|
|
32
|
+
.command("tokens")
|
|
33
|
+
.description("List supported TIP-20 tokens (symbol, decimals, address)")
|
|
34
|
+
.action(tokens);
|
|
30
35
|
program
|
|
31
36
|
.command("balance")
|
|
32
|
-
.description("Show
|
|
33
|
-
.option("--token <address>", "Token contract (default:
|
|
37
|
+
.description("Show TIP-20 token balances of the paired account")
|
|
38
|
+
.option("--token <symbol|address>", "Token symbol (pathUSD, THCFI, THCOC) or contract address (default: all)")
|
|
34
39
|
.action(balance);
|
|
35
40
|
program
|
|
36
41
|
.command("transfer")
|
|
37
42
|
.description("Transfer tokens signed by the agent access key (spend limit enforced on-chain)")
|
|
38
43
|
.argument("<to>", "Recipient address (0x…)")
|
|
39
44
|
.argument("<amount>", "Amount in human units (e.g. 1.5)")
|
|
40
|
-
.option("--token <address>", "Token contract (default: pathUSD)")
|
|
45
|
+
.option("--token <symbol|address>", "Token symbol or contract address (default: pathUSD)")
|
|
41
46
|
.action(transfer);
|
|
42
47
|
program
|
|
43
48
|
.command("fund")
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** TIP-20 tokens surfaced by the CLI — all ThaiFi TIP-20s use 6 decimals. */
|
|
2
|
+
import { CONFIG } from "./config.js";
|
|
3
|
+
export const PATHUSD = {
|
|
4
|
+
address: CONFIG.pathUsd,
|
|
5
|
+
symbol: "pathUSD",
|
|
6
|
+
name: "pathUSD",
|
|
7
|
+
decimals: 6,
|
|
8
|
+
};
|
|
9
|
+
export const THCFI = {
|
|
10
|
+
address: "0x185cd24d61D400d4A22469B311B3915d04e10DCF",
|
|
11
|
+
symbol: "THCFI",
|
|
12
|
+
name: "ThaiCoin ThaiFi",
|
|
13
|
+
decimals: 6,
|
|
14
|
+
};
|
|
15
|
+
export const THCOC = {
|
|
16
|
+
address: "0x5c4B54cf00Df2be0169E05DfAF34eE754cace9a4",
|
|
17
|
+
symbol: "THCOC",
|
|
18
|
+
name: "THCOC",
|
|
19
|
+
decimals: 6,
|
|
20
|
+
};
|
|
21
|
+
/** Tokens listed by `thaifi tokens` / shown by default in `thaifi balance`. */
|
|
22
|
+
export const TOKENS = [PATHUSD, THCFI, THCOC];
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a token by symbol (case-insensitive) or 0x address. Addresses
|
|
25
|
+
* outside the registry are treated as custom TIP-20s (6 decimals); the
|
|
26
|
+
* symbol is read from the chain by the caller.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveToken(arg) {
|
|
29
|
+
const bySymbol = TOKENS.find((t) => t.symbol.toLowerCase() === arg.toLowerCase());
|
|
30
|
+
if (bySymbol)
|
|
31
|
+
return bySymbol;
|
|
32
|
+
if (/^0x[0-9a-fA-F]{40}$/.test(arg)) {
|
|
33
|
+
const known = TOKENS.find((t) => t.address.toLowerCase() === arg.toLowerCase());
|
|
34
|
+
if (known)
|
|
35
|
+
return known;
|
|
36
|
+
return { address: arg, symbol: "TOKEN", name: "Custom TIP-20", decimals: 6 };
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`Unknown token "${arg}" — use a symbol (pathUSD, THCFI, THCOC) or a 0x contract address.`);
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thaifi-wallet-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "ThaiFi Wallet CLI for AI agents — pair with wallet.thaifi.com, sign with on-chain access keys (spending limits enforced on-chain), x402/MPP payments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|