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 +7 -5
- package/index.js +2 -2
- package/known-assets.js +37 -1
- package/package.json +4 -4
- package/proxy.js +6 -3
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:
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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
|
|
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` (
|
|
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.
|
|
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
|
|
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
|
-
|
|
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":
|
|
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.
|