@h402/cli 0.1.0 → 0.1.2
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 +4 -2
- package/dist/api.js +3 -3
- package/dist/commands.js +13 -28
- package/dist/config.js +9 -0
- package/dist/help.js +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@ Browsing, quoting, and free-route calls do not require a local wallet. Wallet cr
|
|
|
37
37
|
|
|
38
38
|
Calls hit the production backend (`https://h402.hunt.town`) by default — override with `--api-url` or `H402_API_URL` (e.g. `http://localhost:3000` for local dev).
|
|
39
39
|
|
|
40
|
+
To select a default local signing wallet, add `"defaultWallet": "agent"` to your existing `~/.h402/config.json`, preserving its other fields. This applies to wallet creation, address, balance, funding instructions, authentication, and paid calls. Explicit `--name` or `--wallet` takes precedence; without this setting, the default remains `h402`. If the selected wallet is missing, the CLI reports an error instead of using another wallet. Existing bonus-credit sessions remain associated with the wallet that authenticated them; changing `defaultWallet` does not switch those sessions.
|
|
41
|
+
|
|
40
42
|
## Commands
|
|
41
43
|
|
|
42
44
|
| Command | Description |
|
|
@@ -60,7 +62,7 @@ Run `h402 --help`, `h402 <command> --help`, or `h402 wallet <subcommand> --help`
|
|
|
60
62
|
|
|
61
63
|
| Flag | Applies to | Description |
|
|
62
64
|
| --- | --- | --- |
|
|
63
|
-
| `--name <wallet>` | wallet create/address/balance/fund; auth; call | Wallet to use (default `h402`) |
|
|
65
|
+
| `--name <wallet>` | wallet create/address/balance/fund; auth; call | Wallet to use (default: `defaultWallet` in config, otherwise `h402`) |
|
|
64
66
|
| `--wallet 0x...` | wallet address/balance/fund; auth; call | Sign with the local wallet that owns this address (must exist locally; must agree with `--name` if both are passed) |
|
|
65
67
|
| `--api-url <url>` | auth, credits, search, show, quote, call | Backend base URL override (or `H402_API_URL`; default `https://h402.hunt.town`) |
|
|
66
68
|
| `--json '{...}'` | quote, call | Request body (sets method to POST) |
|
|
@@ -148,7 +150,7 @@ Signing needs no flags for the default passphrase-less wallets. Only when a wall
|
|
|
148
150
|
| `H402_API_URL` | Backend base URL override (or `--api-url`; default `https://h402.hunt.town`) |
|
|
149
151
|
| `H402_WALLET_PASSPHRASE` | Passphrase for passphrase-protected wallets (only needed when the wallet was created with one) |
|
|
150
152
|
|
|
151
|
-
Passphrases are never stored. Wallets are passphrase-less by default; opt in at create time (`--passphrase <s>`, or bare `--passphrase` to be prompted) when a wallet guards meaningful funds. The CLI persists
|
|
153
|
+
Passphrases are never stored. Wallets are passphrase-less by default; opt in at create time (`--passphrase <s>`, or bare `--passphrase` to be prompted) when a wallet guards meaningful funds. The CLI persists the backend URL, session tokens, known wallet addresses, and optional `defaultWallet` and `maxUsd` settings in `~/.h402/config.json`.
|
|
152
154
|
|
|
153
155
|
## Contributing
|
|
154
156
|
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent } from "undici";
|
|
1
|
+
import { Agent, fetch, Headers as UndiciHeaders } from "undici";
|
|
2
2
|
import { CliError } from "./errors.js";
|
|
3
3
|
import { getVersion } from "./help.js";
|
|
4
4
|
import { isRecord } from "./utils.js";
|
|
@@ -22,7 +22,7 @@ function networkErrorMessage(error) {
|
|
|
22
22
|
return error instanceof Error ? error.message : String(error);
|
|
23
23
|
}
|
|
24
24
|
export async function requestJson(backendUrl, path, init = {}) {
|
|
25
|
-
const headers = new
|
|
25
|
+
const headers = new UndiciHeaders(init.headers);
|
|
26
26
|
headers.set("accept", "application/json");
|
|
27
27
|
if (!headers.has("user-agent")) {
|
|
28
28
|
headers.set("user-agent", `h402-cli/${getVersion()}`);
|
|
@@ -57,7 +57,7 @@ export async function requestJson(backendUrl, path, init = {}) {
|
|
|
57
57
|
// parse — keep the raw text so assertOk can surface it instead of throwing here.
|
|
58
58
|
body = text;
|
|
59
59
|
}
|
|
60
|
-
return { backendUrl, url, status: response.status, statusText: response.statusText, body, headers: response.headers };
|
|
60
|
+
return { backendUrl, url, status: response.status, statusText: response.statusText, body, headers: new Headers([...response.headers]) };
|
|
61
61
|
}
|
|
62
62
|
function responseContext(response) {
|
|
63
63
|
return { backendUrl: response.backendUrl, url: response.url };
|
package/dist/commands.js
CHANGED
|
@@ -9,8 +9,8 @@ import { promptPassphrase } from "./prompt.js";
|
|
|
9
9
|
import { assertConcreteProvider, buildProxyPath, encodeRouteId, flagBoolean, flagString, isRecord, mergeH402, parseJsonFlag, parseQueryFlag, printJson, requireValue, resolveMethod } from "./utils.js";
|
|
10
10
|
import { createPaymentSignatureHeader, paymentRequiredFromResponse, selectBaseUsdcRequirement, X402_HEADERS } from "./x402.js";
|
|
11
11
|
const DEFAULT_WALLET_NAME = "h402";
|
|
12
|
-
function walletName(args) {
|
|
13
|
-
return flagString(args.flags, "name", DEFAULT_WALLET_NAME);
|
|
12
|
+
function walletName(args, config) {
|
|
13
|
+
return flagString(args.flags, "name", config.defaultWallet ?? DEFAULT_WALLET_NAME);
|
|
14
14
|
}
|
|
15
15
|
// Explicit passphrase from flags/env. Wallets are passphrase-less by default
|
|
16
16
|
// (the onboarding default), so this is usually undefined and signing simply
|
|
@@ -158,24 +158,7 @@ export async function resolveSigningWallet(args, config) {
|
|
|
158
158
|
config ??= await loadConfig();
|
|
159
159
|
const explicitAddress = flagString(args.flags, "wallet")?.toLowerCase();
|
|
160
160
|
const explicitName = flagString(args.flags, "name");
|
|
161
|
-
if (explicitName) {
|
|
162
|
-
const address = config.wallets[explicitName]?.address?.toLowerCase();
|
|
163
|
-
if (!address) {
|
|
164
|
-
const adopted = await adoptOwsWalletByName(explicitName, config);
|
|
165
|
-
if (adopted) {
|
|
166
|
-
if (explicitAddress && explicitAddress !== adopted.address) {
|
|
167
|
-
throw new Error(`--wallet ${explicitAddress} does not match wallet "${explicitName}" (${adopted.address}). Omit --wallet or pass the wallet that owns this address.`);
|
|
168
|
-
}
|
|
169
|
-
return adopted;
|
|
170
|
-
}
|
|
171
|
-
throw new Error(`No address known for wallet "${explicitName}". Run: h402 wallet create --name ${explicitName}, or h402 wallet restore to re-adopt existing OWS wallets.`);
|
|
172
|
-
}
|
|
173
|
-
if (explicitAddress && explicitAddress !== address) {
|
|
174
|
-
throw new Error(`--wallet ${explicitAddress} does not match wallet "${explicitName}" (${address}). Omit --wallet or pass the wallet that owns this address.`);
|
|
175
|
-
}
|
|
176
|
-
return { name: explicitName, address };
|
|
177
|
-
}
|
|
178
|
-
if (explicitAddress) {
|
|
161
|
+
if (explicitAddress && !explicitName) {
|
|
179
162
|
const owner = Object.entries(config.wallets).find(([, wallet]) => wallet.address?.toLowerCase() === explicitAddress);
|
|
180
163
|
if (!owner) {
|
|
181
164
|
const adopted = await adoptOwsWalletByAddress(explicitAddress, config);
|
|
@@ -185,21 +168,23 @@ export async function resolveSigningWallet(args, config) {
|
|
|
185
168
|
}
|
|
186
169
|
return { name: owner[0], address: explicitAddress };
|
|
187
170
|
}
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
171
|
+
const name = walletName(args, config);
|
|
172
|
+
const address = config.wallets[name]?.address?.toLowerCase();
|
|
173
|
+
const resolved = address ? { name, address } : await adoptOwsWalletByName(name, config);
|
|
174
|
+
if (!resolved) {
|
|
175
|
+
throw new Error(`No address known for wallet "${name}". Run: h402 wallet create --name ${name}, or h402 wallet restore to re-adopt existing OWS wallets.`);
|
|
176
|
+
}
|
|
177
|
+
if (explicitAddress && explicitAddress !== resolved.address) {
|
|
178
|
+
throw new Error(`--wallet ${explicitAddress} does not match wallet "${name}" (${resolved.address}). Omit --wallet or pass the wallet that owns this address.`);
|
|
194
179
|
}
|
|
195
|
-
return
|
|
180
|
+
return resolved;
|
|
196
181
|
}
|
|
197
182
|
export async function walletCommand(args) {
|
|
198
183
|
const subcommand = requireValue(args.positional[1], "wallet subcommand is required");
|
|
199
184
|
rejectExtraPositionals(args, 2, `wallet ${subcommand}`);
|
|
200
|
-
const name = walletName(args);
|
|
201
185
|
const config = await loadConfig();
|
|
202
186
|
if (subcommand === "create") {
|
|
187
|
+
const name = walletName(args, config);
|
|
203
188
|
let wallet;
|
|
204
189
|
try {
|
|
205
190
|
wallet = await createOwsWallet(name, await createPassphrase(args));
|
package/dist/config.js
CHANGED
|
@@ -23,6 +23,12 @@ function normalizeConfig(parsed) {
|
|
|
23
23
|
if (typeof parsed.maxUsd === "string") {
|
|
24
24
|
normalized.maxUsd = parsed.maxUsd;
|
|
25
25
|
}
|
|
26
|
+
if (parsed.defaultWallet !== undefined) {
|
|
27
|
+
if (typeof parsed.defaultWallet !== "string" || !parsed.defaultWallet.trim()) {
|
|
28
|
+
throw new Error("h402 config defaultWallet must be a non-empty wallet name");
|
|
29
|
+
}
|
|
30
|
+
normalized.defaultWallet = parsed.defaultWallet;
|
|
31
|
+
}
|
|
26
32
|
return normalized;
|
|
27
33
|
}
|
|
28
34
|
async function tightenConfigPermissions(file) {
|
|
@@ -69,6 +75,9 @@ function cloneConfig(config) {
|
|
|
69
75
|
if (config.maxUsd !== undefined) {
|
|
70
76
|
cloned.maxUsd = config.maxUsd;
|
|
71
77
|
}
|
|
78
|
+
if (config.defaultWallet !== undefined) {
|
|
79
|
+
cloned.defaultWallet = config.defaultWallet;
|
|
80
|
+
}
|
|
72
81
|
return cloned;
|
|
73
82
|
}
|
|
74
83
|
async function atomicWritePrivateJson(file, config) {
|
package/dist/help.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
// Reusable flag definitions (DRY: declared once, referenced by each command that
|
|
5
5
|
// accepts them — mirrors the README flags table).
|
|
6
6
|
const FLAGS = {
|
|
7
|
-
name: { name: "name", value: "<wallet>", desc: "Wallet to use (default h402)" },
|
|
7
|
+
name: { name: "name", value: "<wallet>", desc: "Wallet to use (default: config.defaultWallet, otherwise h402)" },
|
|
8
8
|
wallet: { name: "wallet", value: "0x...", desc: "Local wallet that owns this address (must agree with --name)" },
|
|
9
9
|
apiUrl: { name: "api-url", value: "<url>", desc: "Backend base URL (or H402_API_URL; default https://h402.hunt.town)" },
|
|
10
10
|
json: { name: "json", value: "'{...}'", desc: "Request body (sets method to POST)" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h402/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Local, non-custodial CLI for h402 — browse the catalog and pay per call in Base USDC over x402 from a local wallet.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"cli"
|
|
35
35
|
],
|
|
36
36
|
"engines": {
|
|
37
|
-
"node": ">=22"
|
|
37
|
+
"node": ">=22.19.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsc",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@h402/core": "^0.1.0",
|
|
49
|
-
"@open-wallet-standard/core": "^1.
|
|
50
|
-
"fs-native-extensions": "^1.5.
|
|
51
|
-
"undici": "^
|
|
49
|
+
"@open-wallet-standard/core": "^1.4.2",
|
|
50
|
+
"fs-native-extensions": "^1.5.1",
|
|
51
|
+
"undici": "^8.10.2"
|
|
52
52
|
}
|
|
53
53
|
}
|