agent402-openclaw 0.5.6 → 0.5.8

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 CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Part of [Agent402](https://agent402.tools), the open-source paid door for AI agents.
4
4
 
5
- Agent402 as an [OpenClaw](https://openclaw.ai) model provider: the auto-routed
6
- tier at a **flat per-call price** and explicit frontier models **priced per
7
- request** from the body, paid in **USDC over x402** from a wallet or with a
5
+ Agent402 as an [OpenClaw](https://openclaw.ai) model provider: explicit models
6
+ **priced per request** from the body by default (metered), and the auto-routed
7
+ tier at a **flat per-call price**, paid in **USDC over x402** from a wallet or with a
8
8
  prepaid credits key (agent402.tools/credits says whether packs are on sale). The
9
9
  same key and gateway reach Agent402's 500+ pay-per-call tools; credits pay
10
10
  Agent402's own services, so the routes that buy from an outside seller
@@ -57,7 +57,9 @@ install from a packed archive and from the npm registry, doctor, setup, model
57
57
  listing, gateway boot and one agent turn (`test-real-install.js`).
58
58
 
59
59
  No OpenClaw? `agent402-openclaw proxy` runs the proxy alone; point any OpenAI
60
- client at `http://127.0.0.1:8412/v1` with model `auto`.
60
+ client at `http://127.0.0.1:8412/v1`. An explicit model rides the metered route
61
+ by default (each request quoted from its body, and settlement never exceeds the
62
+ quote); model `auto` takes the flat routed tier.
61
63
 
62
64
  **Claude Code and other Anthropic-wire clients** (0.5.0): the proxy also answers
63
65
  `POST /v1/messages` and forwards it to the gateway's metered Messages route,
@@ -107,7 +109,7 @@ of a cent and a long one pays for what it asks. `--flat` (or
107
109
  Either way OpenClaw's per-token cost fields stay zero; the price is per call. A model sent to another flat
108
110
  tier's route is priced at its home tier: the 402 quotes that tier's price, and the paid call is served under
109
111
  that tier's caps (the answer says so in `agent402_tier`). The router, grounded and stealth tiers still answer a
110
- 400 naming the right route; nothing is charged. A client-supplied `Idempotency-Key`
112
+ 400 naming the right route; nothing is charged on x402. A client-supplied `Idempotency-Key`
111
113
  is passed through: on a credits key, a retry with the same key replays the paid
112
114
  answer; on a wallet, each forwarded call signs a fresh payment, which the server
113
115
  treats as a new purchase. Without a key, each call is its own payment.
package/index.js CHANGED
@@ -130,7 +130,7 @@ let registeredOnce = false;
130
130
  const plugin = {
131
131
  id: PLUGIN_ID,
132
132
  name: "Agent402",
133
- description: "Agent402 model provider: routed + explicit models at a flat per-call price, paid by card or USDC.",
133
+ description: "Agent402 model provider: routed + explicit models at a flat per-call price, paid in USDC from a wallet, or with a credits key already issued.",
134
134
  register(api) {
135
135
  const cfg = api.pluginConfig || {};
136
136
  const port = Number(cfg.port) || DEFAULT_PORT;
@@ -149,7 +149,7 @@ const plugin = {
149
149
  try { setCachedRoutes(await loadRoutes(upstream, fetch, { pricing })); } catch (e) { log(`[agent402-openclaw] could not read ${upstream}/v1/models yet: ${e?.message || e}`); }
150
150
  started = await startProxy({ upstream, creditsKey, payFetch, port, pricing, log });
151
151
  setActiveProxy(started);
152
- if (started.mode === "unpaid") log("[agent402-openclaw] no payment configured: run `agent402-openclaw setup` (card credits key) - paid calls will answer 402 until then");
152
+ if (started.mode === "unpaid") log("[agent402-openclaw] no payment configured: run `agent402-openclaw setup` (a wallet key, or a credits key already issued) - paid calls will answer 402 until then");
153
153
  },
154
154
  stop: async () => { if (started) { await started.close(); started = null; setActiveProxy(null); } },
155
155
  });
package/known-assets.js CHANGED
@@ -32,10 +32,46 @@ export const KNOWN_ASSETS = Object.freeze({
32
32
  "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": [{ asset: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", decimals: 6, symbol: "USDC" }], // devnet
33
33
  // Stellar: the USDC Stellar Asset Contract (7 decimals on Stellar).
34
34
  "stellar:pubnet": [{ asset: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75", decimals: 7, symbol: "USDC" }],
35
- // Algorand: USDC is ASA 31566704.
35
+ // Algorand: USDC is ASA 31566704. Mainnet is named by its CAIP-2 id (the
36
+ // first 32 characters of the base64 genesis hash) and, by sellers that
37
+ // predate it, by the full hash.
38
+ "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73k": [{ asset: "31566704", decimals: 6, symbol: "USDC" }],
36
39
  "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=": [{ asset: "31566704", decimals: 6, symbol: "USDC" }],
37
40
  });
38
41
 
42
+ /** Chain name -> every network id in the table that names that chain. A
43
+ * chain spelled two ways on the wire lists both, so a buyer's preference by
44
+ * name, or by either spelling, matches a seller's accept in the other. */
45
+ export const NETWORK_NAMES = Object.freeze({
46
+ base: ["eip155:8453"],
47
+ "base-sepolia": ["eip155:84532"],
48
+ polygon: ["eip155:137"],
49
+ arbitrum: ["eip155:42161"],
50
+ monad: ["eip155:143"],
51
+ celo: ["eip155:42220"],
52
+ avalanche: ["eip155:43114"],
53
+ sei: ["eip155:1329"],
54
+ optimism: ["eip155:10"],
55
+ arc: ["eip155:5042"],
56
+ robinhood: ["eip155:4663"],
57
+ solana: ["solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"],
58
+ "solana-devnet": ["solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"],
59
+ stellar: ["stellar:pubnet"],
60
+ algorand: ["algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73k", "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="],
61
+ });
62
+
63
+ /** The network ids a preference names: a chain name's ids; for an id, every
64
+ * id of the chain it belongs to (so either spelling matches the other); an
65
+ * id the table does not know passes through as itself. */
66
+ export function networkIdsOf(pref) {
67
+ const p = String(pref || "").trim();
68
+ if (!p) return [];
69
+ const byName = NETWORK_NAMES[p.toLowerCase()];
70
+ if (byName) return [...byName];
71
+ for (const ids of Object.values(NETWORK_NAMES)) if (ids.some((id) => id.toLowerCase() === p.toLowerCase())) return [...ids];
72
+ return [p];
73
+ }
74
+
39
75
  const EVM_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
40
76
  // EVM addresses compare without case (checksum casing is presentation);
41
77
  // base58, Stellar and Algorand identifiers compare exactly.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent402-openclaw",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "OpenClaw model provider for Agent402: an auto-routed tier at a flat per-call price and explicit LLM models priced per request, paid in USDC over x402, or with a prepaid credits key, through a local proxy. Zero dependencies; x402 wallet payment is an optional peer.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -44,9 +44,9 @@
44
44
  "test": "node test.js"
45
45
  },
46
46
  "peerDependencies": {
47
- "@x402/evm": ">=2.20.0",
48
- "@x402/fetch": ">=2.20.0",
49
- "viem": ">=2.48.11"
47
+ "@x402/evm": ">=2.20.0 <3",
48
+ "@x402/fetch": ">=2.20.0 <3",
49
+ "viem": ">=2.48.11 <3"
50
50
  },
51
51
  "peerDependenciesMeta": {
52
52
  "@x402/evm": {
package/proxy.js CHANGED
@@ -6,7 +6,7 @@
6
6
  // SDK pay from a wallet too (ANTHROPIC_BASE_URL=http://127.0.0.1:<port>).
7
7
  //
8
8
  // Two ways to pay, chosen at start:
9
- // creditsKey - a prepaid card-credits key (a402_...) sent as a Bearer; the
9
+ // creditsKey - a prepaid credits key (a402_...) sent as a Bearer; the
10
10
  // gateway authorizes against the balance before the handler and
11
11
  // debits only on a final 200. No wallet, no chain.
12
12
  // payFetch - an x402-paying fetch (e.g. @x402/fetch wrapped around a
@@ -45,6 +45,8 @@ import { AUTO_ID, routesFromCatalog, stripTrailingSlashes } from "./models.js";
45
45
 
46
46
  export const DEFAULT_UPSTREAM = "https://agent402.tools";
47
47
  export const PKG_VERSION = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8")).version;
48
+ // The User-Agent product token that names this package on every request.
49
+ const USER_AGENT = `agent402-openclaw/${PKG_VERSION}`;
48
50
  const MAX_BODY = 2 * 1024 * 1024;
49
51
 
50
52
  // The model catalog read (GET /v1/models) settles nothing, so a 503 the
@@ -78,7 +80,8 @@ async function discoveryFetch(fetchImpl, label, url, init) {
78
80
  let attempts = 0;
79
81
  for (;;) {
80
82
  attempts++;
81
- r = await fetchImpl(url, typeof init === "function" ? init() : init);
83
+ const i = (typeof init === "function" ? init() : init) || {};
84
+ r = await fetchImpl(url, { ...i, headers: { "User-Agent": USER_AGENT, ...(i.headers || {}) } });
82
85
  if (r.ok) return r;
83
86
  if (attempts >= DISCOVERY_ATTEMPTS) break;
84
87
  const wait = await shedRetryWaitMs(r);
@@ -177,7 +180,7 @@ export async function startProxy({ upstream = DEFAULT_UPSTREAM, creditsKey = nul
177
180
  // error shape (wire.mapRefusal); every other upstream body is relayed as sent.
178
181
  async function forward(req, res, endpoint, outbound, extraHeaders, wire) {
179
182
  const clientIdem = typeof req.headers["idempotency-key"] === "string" && /^[\w.:-]{8,128}$/.test(req.headers["idempotency-key"]) ? req.headers["idempotency-key"] : null;
180
- const headers = { "content-type": "application/json", accept: req.headers.accept || "application/json", "idempotency-key": clientIdem || randomUUID(), "user-agent": `agent402-openclaw/${PKG_VERSION}`, ...extraHeaders };
183
+ const headers = { "content-type": "application/json", accept: req.headers.accept || "application/json", "idempotency-key": clientIdem || randomUUID(), "user-agent": USER_AGENT, ...extraHeaders };
181
184
  if (key) headers.authorization = `Bearer ${key}`;
182
185
  // A paid request never follows a redirect: followed, it would pay another
183
186
  // origin's 402 or carry a signature (or the credits key) to another host.