agent402-openclaw 0.4.1 → 0.4.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 +3 -0
- package/index.js +42 -5
- package/models.js +7 -1
- package/openclaw.plugin.json +8 -1
- package/package.json +3 -3
- package/provider.js +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# agent402-openclaw
|
|
2
2
|
|
|
3
|
+
Part of [Agent402](https://agent402.tools), the open-source paid door for AI agents.
|
|
4
|
+
|
|
3
5
|
Agent402 as an [OpenClaw](https://openclaw.ai) model provider: auto-routed and
|
|
4
6
|
explicit frontier models at a **flat per-call price**, paid **by card** (a prepaid
|
|
5
7
|
credits key, no wallet) or in **USDC over x402** from a wallet. The same key and
|
|
@@ -46,6 +48,7 @@ client at `http://127.0.0.1:8412/v1` with model `auto`.
|
|
|
46
48
|
```bash
|
|
47
49
|
npm i @x402/fetch @x402/evm viem
|
|
48
50
|
export AGENT402_WALLET_KEY=0x... # an EVM key holding USDC on Base
|
|
51
|
+
export AGENT402_MAX_PER_CALL_USD=2 # per-call ceiling checked before signing (default 2, the gateway's metered cap; 0 disables)
|
|
49
52
|
```
|
|
50
53
|
|
|
51
54
|
With no credits key present the proxy signs an x402 payment per call from that
|
package/index.js
CHANGED
|
@@ -47,10 +47,44 @@ export const PERMIT2_READY_MIN = 10n ** 12n; // 1,000,000 USDC (6 decimals): a r
|
|
|
47
47
|
|
|
48
48
|
/** Which 402 accept to pay: upto on Base when the client can (settle-actual),
|
|
49
49
|
* else exact on Base, else the first the client supports. Pure. */
|
|
50
|
-
|
|
50
|
+
/** Per-call ceiling in USD, enforced in the accept selector so an over-cap quote
|
|
51
|
+
* is refused BEFORE anything is signed. @x402/core 2.23+ ships a $1
|
|
52
|
+
* pegged-assets default on every client; this package turns that off (it would
|
|
53
|
+
* refuse USDG and gave an opaque vendor error on any metered turn over $1) and
|
|
54
|
+
* carries its own ceiling under its own name. The default is $2, the gateway's
|
|
55
|
+
* own metered quote cap (GATEWAY_METERED_MAX_QUOTE_USD), so no call the gateway
|
|
56
|
+
* would serve is refused here while a runaway quote still stops at the wallet.
|
|
57
|
+
* `maxPerCallUsd` in the plugin config or AGENT402_MAX_PER_CALL_USD; `0`/`off`
|
|
58
|
+
* disables; a malformed value reads as the default, never as off. */
|
|
59
|
+
export const DEFAULT_MAX_PER_CALL_USD = 2;
|
|
60
|
+
export function maxPerCallUsd(pluginConfig = {}) {
|
|
61
|
+
const raw = String(pluginConfig.maxPerCallUsd ?? process.env.AGENT402_MAX_PER_CALL_USD ?? "").trim().toLowerCase();
|
|
62
|
+
if (raw === "0" || raw === "off") return Infinity;
|
|
63
|
+
const n = Number(raw);
|
|
64
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_MAX_PER_CALL_USD;
|
|
65
|
+
}
|
|
66
|
+
/** Dollar value of an accept's quote: `amount` (v2) or `maxAmountRequired` (v1)
|
|
67
|
+
* in base units over the asset's `extra.decimals` (USDC on Base: 6). Unknown
|
|
68
|
+
* shape reads as null, and null is REFUSED under a finite ceiling: a quote we
|
|
69
|
+
* cannot read is not one we sign. */
|
|
70
|
+
export function acceptUsd(accept) {
|
|
71
|
+
const raw = accept?.amount ?? accept?.maxAmountRequired;
|
|
72
|
+
if (raw == null || !/^\d+$/.test(String(raw))) return null;
|
|
73
|
+
const dec = Number(accept?.extra?.decimals ?? 6);
|
|
74
|
+
if (!Number.isInteger(dec) || dec < 0 || dec > 36) return null;
|
|
75
|
+
return Number(raw) / 10 ** dec;
|
|
76
|
+
}
|
|
77
|
+
export function selectAccept(accepts, { preferUpto = true, maxUsd = Infinity } = {}) {
|
|
51
78
|
const list = Array.isArray(accepts) ? accepts : [];
|
|
52
|
-
|
|
53
|
-
|
|
79
|
+
const pick = (preferUpto && list.find((a) => a?.scheme === "upto" && a?.network === BASE_CAIP2))
|
|
80
|
+
|| list.find((a) => a?.scheme === "exact" && a?.network === BASE_CAIP2) || list[0];
|
|
81
|
+
if (pick && Number.isFinite(maxUsd)) {
|
|
82
|
+
const usd = acceptUsd(pick);
|
|
83
|
+
if (usd == null || usd > maxUsd) {
|
|
84
|
+
throw new Error(`agent402-openclaw: quote ${usd == null ? "unreadable" : `$${usd}`} exceeds the per-call ceiling $${maxUsd} (maxPerCallUsd / AGENT402_MAX_PER_CALL_USD; 0 disables)`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return pick;
|
|
54
88
|
}
|
|
55
89
|
export const uptoReady = (allowance) => { try { return BigInt(allowance ?? 0) >= PERMIT2_READY_MIN; } catch { return false; } };
|
|
56
90
|
|
|
@@ -70,7 +104,9 @@ export async function resolvePayFetch(pluginConfig = {}, log = () => {}) {
|
|
|
70
104
|
const pub = createPublicClient({ chain: base, transport: http((pluginConfig.baseRpc || process.env.AGENT402_BASE_RPC || DEFAULT_BASE_RPC).trim()) });
|
|
71
105
|
const allowance = await pub.readContract(getPermit2AllowanceReadParams({ tokenAddress: USDC_BASE, ownerAddress: account.address }));
|
|
72
106
|
upto = uptoReady(allowance);
|
|
73
|
-
const
|
|
107
|
+
const maxUsd = maxPerCallUsd(pluginConfig);
|
|
108
|
+
const client = new x402Client((v, accepts) => selectAccept(accepts, { preferUpto: upto, maxUsd }));
|
|
109
|
+
client.setSpendControls?.(false); // the vendor default ($1, pegged assets only) would refuse USDG and any quote over $1; selectAccept enforces our own maxPerCallUsd (same $1 default) instead
|
|
74
110
|
registerExactEvmScheme(client, { signer });
|
|
75
111
|
if (upto) client.register(BASE_CAIP2, new UptoEvmScheme(signer));
|
|
76
112
|
log(upto
|
|
@@ -80,7 +116,8 @@ export async function resolvePayFetch(pluginConfig = {}, log = () => {}) {
|
|
|
80
116
|
} catch (e) {
|
|
81
117
|
// The allowance read or the upto scheme failed: exact still works.
|
|
82
118
|
log(`[agent402-openclaw] upto unavailable (${String(e?.message || e).slice(0, 120)}) - paying exact`);
|
|
83
|
-
const client = new x402Client((v, accepts) => selectAccept(accepts, { preferUpto: false }));
|
|
119
|
+
const client = new x402Client((v, accepts) => selectAccept(accepts, { preferUpto: false, maxUsd: maxPerCallUsd(pluginConfig) }));
|
|
120
|
+
client.setSpendControls?.(false); // the vendor default ($1, pegged assets only) would refuse USDG and any quote over $1; selectAccept enforces our own maxPerCallUsd (same $1 default) instead
|
|
84
121
|
registerExactEvmScheme(client, { signer });
|
|
85
122
|
return wrapFetchWithPayment(fetch, client);
|
|
86
123
|
}
|
package/models.js
CHANGED
|
@@ -77,7 +77,13 @@ export function routesFromCatalog(catalog, { pricing = "metered" } = {}) {
|
|
|
77
77
|
|
|
78
78
|
/** Preferred primaries, in order: capable tool-callers that are cheap when
|
|
79
79
|
* metered. Only consulted among routes that can hold OpenClaw's prompt. */
|
|
80
|
-
|
|
80
|
+
// Exported so the gateway's live-catalog guard (scripts/test-gateway-model-ids.js)
|
|
81
|
+
// fails the day any of these ids carries an expiration date upstream: the first
|
|
82
|
+
// pick is haiku-4.5, whose retirement floor is 2026-10-15 with no notice yet.
|
|
83
|
+
// google/gemini-2.5-flash left this list 2026-09-18: the live catalog stamps it
|
|
84
|
+
// expiration_date 2026-10-20, which is exactly what that guard was written to
|
|
85
|
+
// catch on its first run.
|
|
86
|
+
export const PRIMARY_PREFERENCE = ["anthropic/claude-haiku-4.5", "openai/gpt-4.1-mini", "openai/gpt-4o-mini"];
|
|
81
87
|
|
|
82
88
|
/** The route OpenClaw should make its primary model: the first preferred id
|
|
83
89
|
* that fits OPENCLAW_MIN_INPUT_CHARS, else the cheapest fitting one, else
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "agent402
|
|
2
|
+
"id": "agent402",
|
|
3
3
|
"name": "Agent402",
|
|
4
4
|
"description": "Agent402 model provider: auto-routed and explicit frontier models at a flat per-call price, paid by card (prepaid credits key) or in USDC over x402. Same key reaches 500+ pay-per-call tools.",
|
|
5
5
|
"activation": {
|
|
@@ -36,6 +36,13 @@
|
|
|
36
36
|
"flat"
|
|
37
37
|
],
|
|
38
38
|
"description": "metered (default): explicit models pay a per-request quote; flat: every model on its flat tier."
|
|
39
|
+
},
|
|
40
|
+
"maxPerCallUsd": {
|
|
41
|
+
"type": [
|
|
42
|
+
"number",
|
|
43
|
+
"string"
|
|
44
|
+
],
|
|
45
|
+
"description": "Per-call ceiling in USD for wallet (x402) payments, checked before signing (default 2, the gateway's metered quote cap; 0 or \"off\" disables). Also AGENT402_MAX_PER_CALL_USD."
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent402-openclaw",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "OpenClaw model provider for Agent402: auto-routed and explicit LLM models at a flat per-call price, paid by card (prepaid credits key) or in USDC over x402 through a local proxy. Zero dependencies; x402 wallet payment is an optional peer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"./index.js"
|
|
27
27
|
],
|
|
28
28
|
"compat": {
|
|
29
|
-
"pluginApi": ">=2026.
|
|
29
|
+
"pluginApi": ">=2026.7.1"
|
|
30
30
|
},
|
|
31
31
|
"build": {
|
|
32
32
|
"openclawVersion": "2026.8.1"
|
|
33
33
|
},
|
|
34
34
|
"install": {
|
|
35
|
-
"minHostVersion": ">=2026.
|
|
35
|
+
"minHostVersion": ">=2026.7.1",
|
|
36
36
|
"npmSpec": "agent402-openclaw"
|
|
37
37
|
}
|
|
38
38
|
},
|
package/provider.js
CHANGED
|
@@ -14,7 +14,7 @@ export const PROVIDER_ID = "agent402";
|
|
|
14
14
|
// started checking. Writing config under the provider id while the host reads
|
|
15
15
|
// it under the manifest id is the same defect as the 2026-08-26 --port bug,
|
|
16
16
|
// where setup moved the baseUrl and the service kept reading the old key.
|
|
17
|
-
export const PLUGIN_ID = "agent402
|
|
17
|
+
export const PLUGIN_ID = "agent402";
|
|
18
18
|
export const DEFAULT_PORT = 8412;
|
|
19
19
|
|
|
20
20
|
let activeProxy = null;
|