@blockrun/clawrouter 0.12.275 → 0.12.277
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 +17 -1
- package/dist/cli.js +178 -2
- package/dist/index.js +177 -2
- package/docs/configuration.md +64 -0
- package/package.json +4 -1
- package/src/top-models.json +1 -0
package/README.md
CHANGED
|
@@ -540,7 +540,8 @@ No Surf account, no API key — settles directly to Surf's Base treasury in USDC
|
|
|
540
540
|
| zai/glm-5.1 | $1.40 | $4.40 | $0.0029 | 200K | reasoning, tools (promo ended 2026-06-05) |
|
|
541
541
|
| qwen/qwen3.7-max | $1.475 | $4.425 | $0.0030 | 1M | reasoning, agentic, tools (Qwen flagship) |
|
|
542
542
|
| xai/grok-4.3 | $1.25 | $2.50 | $0.0019 | 1M | reasoning, vision, agentic, tools |
|
|
543
|
-
| google/gemini-3.
|
|
543
|
+
| google/gemini-3.8-flash | $0.75 | $3.75 | $0.0023 | 1M | reasoning, vision, tools (newest Flash) |
|
|
544
|
+
| google/gemini-3.6-flash | $0.75 | $3.75 | $0.0023 | 1M | reasoning, vision, tools (thinking mode) |
|
|
544
545
|
| google/gemini-3.5-flash | $1.50 | $9.00 | $0.0053 | 1M | reasoning, vision, tools (thinking) |
|
|
545
546
|
| xai/grok-4.5 | $2.00 | $6.00 | $0.0040 | 500K | reasoning, vision, agentic, tools |
|
|
546
547
|
| xai/grok-build-0.1 | $1.00 | $2.00 | $0.0015 | 256K | agentic coding, tools |
|
|
@@ -766,9 +767,24 @@ For basic usage, no configuration needed. For advanced options:
|
|
|
766
767
|
| `CLAWROUTER_DISABLED` | `false` | Disable smart routing |
|
|
767
768
|
| `CLAWROUTER_DEBUG_HEADERS` | `on` | Set to `off` to suppress `x-clawrouter-*` debug response headers |
|
|
768
769
|
| `CLAWROUTER_SOLANA_RPC_URL` | `https://api.mainnet-beta.solana.com` | Solana RPC endpoint — balance checks and payment signing |
|
|
770
|
+
| `TWZRD_AUTO_GATE` | unset (off) | Set to `1` to compose TWZRD AutoGate after SpendControl on the x402 pre-sign hook |
|
|
769
771
|
|
|
770
772
|
**Full reference:** [docs/configuration.md](docs/configuration.md)
|
|
771
773
|
|
|
774
|
+
### Pre-spend trust (TWZRD AutoGate, opt-in)
|
|
775
|
+
|
|
776
|
+
Default **off**. Spend limits and counterparty lists (`clawrouter policy`) stay the vendor-neutral path. This is not a default-on vendor lock.
|
|
777
|
+
|
|
778
|
+
To also run TWZRD wash/preflight on the same `onBeforePaymentCreation` chain — after SpendControl, before the wallet signs:
|
|
779
|
+
|
|
780
|
+
```bash
|
|
781
|
+
export TWZRD_AUTO_GATE=1
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
Equivalent flag: `TWZRD_GATE_ENABLED=true`. Requires optional dependency `twzrd-x402-gate@0.9.3` (forks that omit it still install). A refuse stamps `X-Twzrd-Caller: clawrouter/<version>` so it is attributable. Unset the flag to go back to SpendControl only.
|
|
785
|
+
|
|
786
|
+
**It gates Solana, not Base.** The gate scores Solana recipients; Base/EVM classifies as `network_not_scored` and is waved through without a lookup. On Solana it POSTs the resource URL, `payTo`, price and chain to `intel.twzrd.xyz` before each signature — payment metadata leaves your machine when this is on. We bound that call at `TWZRD_GATE_TIMEOUT_MS` (default 2000) and proceed if it does not answer, because SpendControl is the guarantee and a third party must not be able to stop your payments; set `TWZRD_FAIL_OPEN=false` to refuse instead.
|
|
787
|
+
|
|
772
788
|
### Model Exclusion
|
|
773
789
|
|
|
774
790
|
Block specific models from being routed to. Useful if a model doesn't follow your agent instructions or you want to control costs.
|
package/dist/cli.js
CHANGED
|
@@ -37890,6 +37890,7 @@ var init_top_models = __esm({
|
|
|
37890
37890
|
"openai/gpt-5.4-nano",
|
|
37891
37891
|
"openai/gpt-5.3-codex",
|
|
37892
37892
|
"google/gemini-3.1-pro",
|
|
37893
|
+
"google/gemini-3.8-flash",
|
|
37893
37894
|
"google/gemini-3.6-flash",
|
|
37894
37895
|
"google/gemini-3.5-flash",
|
|
37895
37896
|
"google/gemini-3.5-flash-lite",
|
|
@@ -39073,9 +39074,38 @@ var init_models = __esm({
|
|
|
39073
39074
|
vision: true,
|
|
39074
39075
|
toolCalling: true
|
|
39075
39076
|
},
|
|
39077
|
+
{
|
|
39078
|
+
// Current-generation Flash. Live in the BlockRun catalog (chat, reasoning,
|
|
39079
|
+
// coding, vision; $0.75/$3.75) and pinned from the Hermes picker, so it
|
|
39080
|
+
// MUST be carried here and not just be routable at the gateway:
|
|
39081
|
+
// estimateAmount() returns undefined for an id we do not catalog, which
|
|
39082
|
+
// skips the pre-request balance check, projects $0 into the strict
|
|
39083
|
+
// maxCostPerRun gate and never accumulates into session cost. The gateway
|
|
39084
|
+
// ships 3.8 with the same pricing, context and capability row as 3.6.
|
|
39085
|
+
id: "google/gemini-3.8-flash",
|
|
39086
|
+
name: "Gemini 3.8 Flash",
|
|
39087
|
+
version: "3.8",
|
|
39088
|
+
// ⏰ PROMOTIONAL RATE, expires 2027-01-01. Google prices the whole 3.6/3.7/3.8
|
|
39089
|
+
// Flash band at 0.75/3.75 only through 2026-12-31, reverting to 1.50/7.50
|
|
39090
|
+
// (ai.google.dev/gemini-api/docs/pricing; blockrun's src/lib/models.ts carries
|
|
39091
|
+
// the same dated note). These numbers are not decoration here: calculateModelCost
|
|
39092
|
+
// feeds the maxCostPerRun projection and every `cost` in the usage journal, so
|
|
39093
|
+
// leaving them at the promo rate past the reversion under-reports spend 2x and
|
|
39094
|
+
// lets the cap run to twice its stated limit. Re-price this AND gemini-3.6-flash
|
|
39095
|
+
// together — they revert on the same day.
|
|
39096
|
+
inputPrice: 0.75,
|
|
39097
|
+
outputPrice: 3.75,
|
|
39098
|
+
contextWindow: 1048576,
|
|
39099
|
+
maxOutput: 65536,
|
|
39100
|
+
reasoning: true,
|
|
39101
|
+
vision: true,
|
|
39102
|
+
toolCalling: true
|
|
39103
|
+
},
|
|
39076
39104
|
{
|
|
39077
39105
|
// Newest-generation Flash with built-in thinking mode (blockrun #329,
|
|
39078
39106
|
// 2026-08-03). 17% cheaper output than 3.5 Flash.
|
|
39107
|
+
// ⏰ Same promotional 0.75/3.75 as gemini-3.8-flash above, and the same
|
|
39108
|
+
// 2027-01-01 reversion to 1.50/7.50 — re-price both together.
|
|
39079
39109
|
id: "google/gemini-3.6-flash",
|
|
39080
39110
|
name: "Gemini 3.6 Flash",
|
|
39081
39111
|
version: "3.6",
|
|
@@ -60430,6 +60460,149 @@ var init_api_key = __esm({
|
|
|
60430
60460
|
}
|
|
60431
60461
|
});
|
|
60432
60462
|
|
|
60463
|
+
// src/twzrd-autogate.ts
|
|
60464
|
+
import { randomUUID } from "crypto";
|
|
60465
|
+
function normalizeFlag(value) {
|
|
60466
|
+
const trimmed = value?.trim().toLowerCase();
|
|
60467
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
60468
|
+
}
|
|
60469
|
+
function isTwzrdAutoGateEnabled(env = process.env) {
|
|
60470
|
+
const auto = normalizeFlag(env.TWZRD_AUTO_GATE);
|
|
60471
|
+
const gate = normalizeFlag(env.TWZRD_GATE_ENABLED);
|
|
60472
|
+
if (auto !== void 0 && FALSY.has(auto) || gate !== void 0 && FALSY.has(gate)) {
|
|
60473
|
+
return false;
|
|
60474
|
+
}
|
|
60475
|
+
return auto !== void 0 && TRUTHY.has(auto) || gate !== void 0 && TRUTHY.has(gate);
|
|
60476
|
+
}
|
|
60477
|
+
function isMissingTwzrdGateModule(err) {
|
|
60478
|
+
const code = err?.code;
|
|
60479
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60480
|
+
const missingModule = code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /Cannot find module/i.test(msg) || /Cannot find package/i.test(msg);
|
|
60481
|
+
return missingModule && new RegExp(String.raw`['"]${TWZRD_GATE_PACKAGE}['"]`).test(msg);
|
|
60482
|
+
}
|
|
60483
|
+
function twzrdGateTimeoutMs(env = process.env) {
|
|
60484
|
+
const raw = normalizeFlag(env.TWZRD_GATE_TIMEOUT_MS);
|
|
60485
|
+
if (raw === void 0) return TWZRD_GATE_TIMEOUT_MS;
|
|
60486
|
+
const parsed = Number(raw);
|
|
60487
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : TWZRD_GATE_TIMEOUT_MS;
|
|
60488
|
+
}
|
|
60489
|
+
function twzrdGateFailOpen(env = process.env) {
|
|
60490
|
+
const raw = normalizeFlag(env.TWZRD_FAIL_OPEN);
|
|
60491
|
+
return !(raw !== void 0 && FALSY.has(raw));
|
|
60492
|
+
}
|
|
60493
|
+
function twzrdAutoGateInstallOptions(env = process.env, runId = randomUUID) {
|
|
60494
|
+
return {
|
|
60495
|
+
refuseWashFlagged: true,
|
|
60496
|
+
gateOnCanSpend: false,
|
|
60497
|
+
unsupportedNetworkMode: "observe",
|
|
60498
|
+
failOpen: twzrdGateFailOpen(env),
|
|
60499
|
+
attribution: {
|
|
60500
|
+
integration: `clawrouter/${VERSION}`,
|
|
60501
|
+
runId: runId()
|
|
60502
|
+
}
|
|
60503
|
+
};
|
|
60504
|
+
}
|
|
60505
|
+
async function runTwzrdGateWithTimeout(run, opts) {
|
|
60506
|
+
const log = opts.log ?? console;
|
|
60507
|
+
let timer2;
|
|
60508
|
+
try {
|
|
60509
|
+
const raced = await Promise.race([
|
|
60510
|
+
// Swallow here, not at the race, so a late rejection never goes unhandled.
|
|
60511
|
+
Promise.resolve().then(run).catch((err) => ({ __twzrdError: err })),
|
|
60512
|
+
new Promise((resolve) => {
|
|
60513
|
+
timer2 = setTimeout(() => resolve(TIMED_OUT), opts.timeoutMs);
|
|
60514
|
+
})
|
|
60515
|
+
]);
|
|
60516
|
+
if (raced === TIMED_OUT) {
|
|
60517
|
+
return decideOnGateFailure(`did not answer within ${opts.timeoutMs}ms`, opts.failOpen, log);
|
|
60518
|
+
}
|
|
60519
|
+
if (raced && typeof raced === "object" && "__twzrdError" in raced) {
|
|
60520
|
+
const err = raced.__twzrdError;
|
|
60521
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60522
|
+
return decideOnGateFailure(`threw: ${msg.slice(0, 120)}`, opts.failOpen, log);
|
|
60523
|
+
}
|
|
60524
|
+
return raced;
|
|
60525
|
+
} finally {
|
|
60526
|
+
if (timer2) clearTimeout(timer2);
|
|
60527
|
+
}
|
|
60528
|
+
}
|
|
60529
|
+
function decideOnGateFailure(what, failOpen, log) {
|
|
60530
|
+
const reason = `twzrd gate ${what}`;
|
|
60531
|
+
if (failOpen) {
|
|
60532
|
+
log.warn(
|
|
60533
|
+
`[ClawRouter] ${reason} \u2014 proceeding. SpendControl still applied. Set TWZRD_FAIL_OPEN=false to refuse instead.`
|
|
60534
|
+
);
|
|
60535
|
+
return void 0;
|
|
60536
|
+
}
|
|
60537
|
+
log.warn(`[ClawRouter] ${reason} \u2014 refusing the payment (TWZRD_FAIL_OPEN=false).`);
|
|
60538
|
+
return { abort: true, reason };
|
|
60539
|
+
}
|
|
60540
|
+
async function defaultLoadTwzrdGate() {
|
|
60541
|
+
const specifier = TWZRD_GATE_PACKAGE;
|
|
60542
|
+
return import(specifier);
|
|
60543
|
+
}
|
|
60544
|
+
async function maybeComposeTwzrdAutoGate(x402, options) {
|
|
60545
|
+
const env = options?.env ?? process.env;
|
|
60546
|
+
const log = options?.log ?? console;
|
|
60547
|
+
if (!isTwzrdAutoGateEnabled(env)) {
|
|
60548
|
+
return { status: "skipped" };
|
|
60549
|
+
}
|
|
60550
|
+
const load = options?.loadGate ?? defaultLoadTwzrdGate;
|
|
60551
|
+
let gate;
|
|
60552
|
+
try {
|
|
60553
|
+
gate = await load();
|
|
60554
|
+
} catch (err) {
|
|
60555
|
+
if (isMissingTwzrdGateModule(err)) {
|
|
60556
|
+
const reason = `${TWZRD_GATE_PACKAGE} is not installed`;
|
|
60557
|
+
log.warn(
|
|
60558
|
+
`[ClawRouter] TWZRD AutoGate opted in but ${reason} \u2014 skipping. npm i ${TWZRD_GATE_PACKAGE}@0.9.3`
|
|
60559
|
+
);
|
|
60560
|
+
return { status: "unavailable", reason };
|
|
60561
|
+
}
|
|
60562
|
+
throw err;
|
|
60563
|
+
}
|
|
60564
|
+
const installOpts = twzrdAutoGateInstallOptions(env);
|
|
60565
|
+
const timeoutMs = twzrdGateTimeoutMs(env);
|
|
60566
|
+
const ready = (via) => log.log(
|
|
60567
|
+
`[ClawRouter] TWZRD AutoGate ON (opt-in, after SpendControl, ${via}; ${timeoutMs}ms budget, failOpen=${installOpts.failOpen}). Unset TWZRD_AUTO_GATE to disable.`
|
|
60568
|
+
);
|
|
60569
|
+
if (typeof gate.createTwzrdBeforePaymentHook === "function") {
|
|
60570
|
+
const hook = gate.createTwzrdBeforePaymentHook(installOpts);
|
|
60571
|
+
x402.onBeforePaymentCreation(
|
|
60572
|
+
async (ctx) => runTwzrdGateWithTimeout(() => hook(ctx.selectedRequirements, ctx), {
|
|
60573
|
+
timeoutMs,
|
|
60574
|
+
failOpen: installOpts.failOpen,
|
|
60575
|
+
log
|
|
60576
|
+
})
|
|
60577
|
+
);
|
|
60578
|
+
ready("createTwzrdBeforePaymentHook");
|
|
60579
|
+
return { status: "composed", via: "createTwzrdBeforePaymentHook" };
|
|
60580
|
+
}
|
|
60581
|
+
if (typeof gate.installTwzrdAutoGate === "function") {
|
|
60582
|
+
gate.installTwzrdAutoGate(x402, installOpts);
|
|
60583
|
+
log.warn(
|
|
60584
|
+
"[ClawRouter] TWZRD AutoGate installed via installTwzrdAutoGate \u2014 that entry point registers its own hook, so the timeout budget does not apply and it replaces onBeforePaymentCreation on this client. Prefer a gate build exporting createTwzrdBeforePaymentHook."
|
|
60585
|
+
);
|
|
60586
|
+
ready("installTwzrdAutoGate");
|
|
60587
|
+
return { status: "composed", via: "installTwzrdAutoGate" };
|
|
60588
|
+
}
|
|
60589
|
+
throw new Error(
|
|
60590
|
+
`[ClawRouter] ${TWZRD_GATE_PACKAGE} is installed but exports neither installTwzrdAutoGate nor createTwzrdBeforePaymentHook`
|
|
60591
|
+
);
|
|
60592
|
+
}
|
|
60593
|
+
var TWZRD_GATE_PACKAGE, TRUTHY, FALSY, TWZRD_GATE_TIMEOUT_MS, TIMED_OUT;
|
|
60594
|
+
var init_twzrd_autogate = __esm({
|
|
60595
|
+
"src/twzrd-autogate.ts"() {
|
|
60596
|
+
"use strict";
|
|
60597
|
+
init_version4();
|
|
60598
|
+
TWZRD_GATE_PACKAGE = "twzrd-x402-gate";
|
|
60599
|
+
TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
60600
|
+
FALSY = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
|
|
60601
|
+
TWZRD_GATE_TIMEOUT_MS = 2e3;
|
|
60602
|
+
TIMED_OUT = { __twzrdTimedOut: true };
|
|
60603
|
+
}
|
|
60604
|
+
});
|
|
60605
|
+
|
|
60433
60606
|
// src/compression/types.ts
|
|
60434
60607
|
var DEFAULT_COMPRESSION_CONFIG;
|
|
60435
60608
|
var init_types2 = __esm({
|
|
@@ -92014,6 +92187,7 @@ async function startProxy(options) {
|
|
|
92014
92187
|
const evmSigner = toClientEvmSigner(account, evmPublicClient);
|
|
92015
92188
|
const spendControl = options.spendControl ?? getSharedSpendControl();
|
|
92016
92189
|
registerSpendPolicyHook(x402, spendControl);
|
|
92190
|
+
await maybeComposeTwzrdAutoGate(x402);
|
|
92017
92191
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
92018
92192
|
}
|
|
92019
92193
|
let solanaAddress;
|
|
@@ -95599,6 +95773,7 @@ var init_proxy = __esm({
|
|
|
95599
95773
|
init_auth();
|
|
95600
95774
|
init_api_key();
|
|
95601
95775
|
init_spend_control();
|
|
95776
|
+
init_twzrd_autogate();
|
|
95602
95777
|
init_compression();
|
|
95603
95778
|
init_errors5();
|
|
95604
95779
|
init_version4();
|
|
@@ -228656,7 +228831,7 @@ import {
|
|
|
228656
228831
|
} from "fs";
|
|
228657
228832
|
import { readFile as readFileAsync } from "fs/promises";
|
|
228658
228833
|
import { homedir as homedir15 } from "os";
|
|
228659
|
-
import { randomUUID } from "crypto";
|
|
228834
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
228660
228835
|
import { join as join18 } from "path";
|
|
228661
228836
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
228662
228837
|
const start = Date.now();
|
|
@@ -228671,7 +228846,7 @@ async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
|
228671
228846
|
return false;
|
|
228672
228847
|
}
|
|
228673
228848
|
function writePrivateJsonSync(path5, value) {
|
|
228674
|
-
const temporary = `${path5}.tmp.${
|
|
228849
|
+
const temporary = `${path5}.tmp.${randomUUID2()}`;
|
|
228675
228850
|
try {
|
|
228676
228851
|
writeFileSync3(temporary, JSON.stringify(value, null, 2), { mode: 384 });
|
|
228677
228852
|
renameSync2(temporary, path5);
|
|
@@ -231162,6 +231337,7 @@ Environment Variables:
|
|
|
231162
231337
|
BLOCKRUN_API_KEY BlockRun API key (brk_...) \u2014 wins over the wallet when set
|
|
231163
231338
|
BLOCKRUN_WALLET_KEY Private key for x402 payments (auto-generated if not set)
|
|
231164
231339
|
BLOCKRUN_PROXY_PORT Default proxy port (default: 8402)
|
|
231340
|
+
TWZRD_AUTO_GATE Set to 1 to compose TWZRD AutoGate after SpendControl (default off)
|
|
231165
231341
|
|
|
231166
231342
|
For more info: https://blockrun.ai/clawrouter.md
|
|
231167
231343
|
`);
|
package/dist/index.js
CHANGED
|
@@ -75,6 +75,7 @@ var init_top_models = __esm({
|
|
|
75
75
|
"openai/gpt-5.4-nano",
|
|
76
76
|
"openai/gpt-5.3-codex",
|
|
77
77
|
"google/gemini-3.1-pro",
|
|
78
|
+
"google/gemini-3.8-flash",
|
|
78
79
|
"google/gemini-3.6-flash",
|
|
79
80
|
"google/gemini-3.5-flash",
|
|
80
81
|
"google/gemini-3.5-flash-lite",
|
|
@@ -1258,9 +1259,38 @@ var init_models = __esm({
|
|
|
1258
1259
|
vision: true,
|
|
1259
1260
|
toolCalling: true
|
|
1260
1261
|
},
|
|
1262
|
+
{
|
|
1263
|
+
// Current-generation Flash. Live in the BlockRun catalog (chat, reasoning,
|
|
1264
|
+
// coding, vision; $0.75/$3.75) and pinned from the Hermes picker, so it
|
|
1265
|
+
// MUST be carried here and not just be routable at the gateway:
|
|
1266
|
+
// estimateAmount() returns undefined for an id we do not catalog, which
|
|
1267
|
+
// skips the pre-request balance check, projects $0 into the strict
|
|
1268
|
+
// maxCostPerRun gate and never accumulates into session cost. The gateway
|
|
1269
|
+
// ships 3.8 with the same pricing, context and capability row as 3.6.
|
|
1270
|
+
id: "google/gemini-3.8-flash",
|
|
1271
|
+
name: "Gemini 3.8 Flash",
|
|
1272
|
+
version: "3.8",
|
|
1273
|
+
// ⏰ PROMOTIONAL RATE, expires 2027-01-01. Google prices the whole 3.6/3.7/3.8
|
|
1274
|
+
// Flash band at 0.75/3.75 only through 2026-12-31, reverting to 1.50/7.50
|
|
1275
|
+
// (ai.google.dev/gemini-api/docs/pricing; blockrun's src/lib/models.ts carries
|
|
1276
|
+
// the same dated note). These numbers are not decoration here: calculateModelCost
|
|
1277
|
+
// feeds the maxCostPerRun projection and every `cost` in the usage journal, so
|
|
1278
|
+
// leaving them at the promo rate past the reversion under-reports spend 2x and
|
|
1279
|
+
// lets the cap run to twice its stated limit. Re-price this AND gemini-3.6-flash
|
|
1280
|
+
// together — they revert on the same day.
|
|
1281
|
+
inputPrice: 0.75,
|
|
1282
|
+
outputPrice: 3.75,
|
|
1283
|
+
contextWindow: 1048576,
|
|
1284
|
+
maxOutput: 65536,
|
|
1285
|
+
reasoning: true,
|
|
1286
|
+
vision: true,
|
|
1287
|
+
toolCalling: true
|
|
1288
|
+
},
|
|
1261
1289
|
{
|
|
1262
1290
|
// Newest-generation Flash with built-in thinking mode (blockrun #329,
|
|
1263
1291
|
// 2026-08-03). 17% cheaper output than 3.5 Flash.
|
|
1292
|
+
// ⏰ Same promotional 0.75/3.75 as gemini-3.8-flash above, and the same
|
|
1293
|
+
// 2027-01-01 reversion to 1.50/7.50 — re-price both together.
|
|
1264
1294
|
id: "google/gemini-3.6-flash",
|
|
1265
1295
|
name: "Gemini 3.6 Flash",
|
|
1266
1296
|
version: "3.6",
|
|
@@ -60183,6 +60213,149 @@ var init_api_key = __esm({
|
|
|
60183
60213
|
}
|
|
60184
60214
|
});
|
|
60185
60215
|
|
|
60216
|
+
// src/twzrd-autogate.ts
|
|
60217
|
+
import { randomUUID } from "crypto";
|
|
60218
|
+
function normalizeFlag(value) {
|
|
60219
|
+
const trimmed = value?.trim().toLowerCase();
|
|
60220
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
60221
|
+
}
|
|
60222
|
+
function isTwzrdAutoGateEnabled(env = process.env) {
|
|
60223
|
+
const auto = normalizeFlag(env.TWZRD_AUTO_GATE);
|
|
60224
|
+
const gate = normalizeFlag(env.TWZRD_GATE_ENABLED);
|
|
60225
|
+
if (auto !== void 0 && FALSY.has(auto) || gate !== void 0 && FALSY.has(gate)) {
|
|
60226
|
+
return false;
|
|
60227
|
+
}
|
|
60228
|
+
return auto !== void 0 && TRUTHY.has(auto) || gate !== void 0 && TRUTHY.has(gate);
|
|
60229
|
+
}
|
|
60230
|
+
function isMissingTwzrdGateModule(err) {
|
|
60231
|
+
const code = err?.code;
|
|
60232
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60233
|
+
const missingModule = code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /Cannot find module/i.test(msg) || /Cannot find package/i.test(msg);
|
|
60234
|
+
return missingModule && new RegExp(String.raw`['"]${TWZRD_GATE_PACKAGE}['"]`).test(msg);
|
|
60235
|
+
}
|
|
60236
|
+
function twzrdGateTimeoutMs(env = process.env) {
|
|
60237
|
+
const raw = normalizeFlag(env.TWZRD_GATE_TIMEOUT_MS);
|
|
60238
|
+
if (raw === void 0) return TWZRD_GATE_TIMEOUT_MS;
|
|
60239
|
+
const parsed = Number(raw);
|
|
60240
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : TWZRD_GATE_TIMEOUT_MS;
|
|
60241
|
+
}
|
|
60242
|
+
function twzrdGateFailOpen(env = process.env) {
|
|
60243
|
+
const raw = normalizeFlag(env.TWZRD_FAIL_OPEN);
|
|
60244
|
+
return !(raw !== void 0 && FALSY.has(raw));
|
|
60245
|
+
}
|
|
60246
|
+
function twzrdAutoGateInstallOptions(env = process.env, runId = randomUUID) {
|
|
60247
|
+
return {
|
|
60248
|
+
refuseWashFlagged: true,
|
|
60249
|
+
gateOnCanSpend: false,
|
|
60250
|
+
unsupportedNetworkMode: "observe",
|
|
60251
|
+
failOpen: twzrdGateFailOpen(env),
|
|
60252
|
+
attribution: {
|
|
60253
|
+
integration: `clawrouter/${VERSION}`,
|
|
60254
|
+
runId: runId()
|
|
60255
|
+
}
|
|
60256
|
+
};
|
|
60257
|
+
}
|
|
60258
|
+
async function runTwzrdGateWithTimeout(run, opts) {
|
|
60259
|
+
const log = opts.log ?? console;
|
|
60260
|
+
let timer2;
|
|
60261
|
+
try {
|
|
60262
|
+
const raced = await Promise.race([
|
|
60263
|
+
// Swallow here, not at the race, so a late rejection never goes unhandled.
|
|
60264
|
+
Promise.resolve().then(run).catch((err) => ({ __twzrdError: err })),
|
|
60265
|
+
new Promise((resolve) => {
|
|
60266
|
+
timer2 = setTimeout(() => resolve(TIMED_OUT), opts.timeoutMs);
|
|
60267
|
+
})
|
|
60268
|
+
]);
|
|
60269
|
+
if (raced === TIMED_OUT) {
|
|
60270
|
+
return decideOnGateFailure(`did not answer within ${opts.timeoutMs}ms`, opts.failOpen, log);
|
|
60271
|
+
}
|
|
60272
|
+
if (raced && typeof raced === "object" && "__twzrdError" in raced) {
|
|
60273
|
+
const err = raced.__twzrdError;
|
|
60274
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60275
|
+
return decideOnGateFailure(`threw: ${msg.slice(0, 120)}`, opts.failOpen, log);
|
|
60276
|
+
}
|
|
60277
|
+
return raced;
|
|
60278
|
+
} finally {
|
|
60279
|
+
if (timer2) clearTimeout(timer2);
|
|
60280
|
+
}
|
|
60281
|
+
}
|
|
60282
|
+
function decideOnGateFailure(what, failOpen, log) {
|
|
60283
|
+
const reason = `twzrd gate ${what}`;
|
|
60284
|
+
if (failOpen) {
|
|
60285
|
+
log.warn(
|
|
60286
|
+
`[ClawRouter] ${reason} \u2014 proceeding. SpendControl still applied. Set TWZRD_FAIL_OPEN=false to refuse instead.`
|
|
60287
|
+
);
|
|
60288
|
+
return void 0;
|
|
60289
|
+
}
|
|
60290
|
+
log.warn(`[ClawRouter] ${reason} \u2014 refusing the payment (TWZRD_FAIL_OPEN=false).`);
|
|
60291
|
+
return { abort: true, reason };
|
|
60292
|
+
}
|
|
60293
|
+
async function defaultLoadTwzrdGate() {
|
|
60294
|
+
const specifier = TWZRD_GATE_PACKAGE;
|
|
60295
|
+
return import(specifier);
|
|
60296
|
+
}
|
|
60297
|
+
async function maybeComposeTwzrdAutoGate(x402, options) {
|
|
60298
|
+
const env = options?.env ?? process.env;
|
|
60299
|
+
const log = options?.log ?? console;
|
|
60300
|
+
if (!isTwzrdAutoGateEnabled(env)) {
|
|
60301
|
+
return { status: "skipped" };
|
|
60302
|
+
}
|
|
60303
|
+
const load = options?.loadGate ?? defaultLoadTwzrdGate;
|
|
60304
|
+
let gate;
|
|
60305
|
+
try {
|
|
60306
|
+
gate = await load();
|
|
60307
|
+
} catch (err) {
|
|
60308
|
+
if (isMissingTwzrdGateModule(err)) {
|
|
60309
|
+
const reason = `${TWZRD_GATE_PACKAGE} is not installed`;
|
|
60310
|
+
log.warn(
|
|
60311
|
+
`[ClawRouter] TWZRD AutoGate opted in but ${reason} \u2014 skipping. npm i ${TWZRD_GATE_PACKAGE}@0.9.3`
|
|
60312
|
+
);
|
|
60313
|
+
return { status: "unavailable", reason };
|
|
60314
|
+
}
|
|
60315
|
+
throw err;
|
|
60316
|
+
}
|
|
60317
|
+
const installOpts = twzrdAutoGateInstallOptions(env);
|
|
60318
|
+
const timeoutMs = twzrdGateTimeoutMs(env);
|
|
60319
|
+
const ready = (via) => log.log(
|
|
60320
|
+
`[ClawRouter] TWZRD AutoGate ON (opt-in, after SpendControl, ${via}; ${timeoutMs}ms budget, failOpen=${installOpts.failOpen}). Unset TWZRD_AUTO_GATE to disable.`
|
|
60321
|
+
);
|
|
60322
|
+
if (typeof gate.createTwzrdBeforePaymentHook === "function") {
|
|
60323
|
+
const hook = gate.createTwzrdBeforePaymentHook(installOpts);
|
|
60324
|
+
x402.onBeforePaymentCreation(
|
|
60325
|
+
async (ctx) => runTwzrdGateWithTimeout(() => hook(ctx.selectedRequirements, ctx), {
|
|
60326
|
+
timeoutMs,
|
|
60327
|
+
failOpen: installOpts.failOpen,
|
|
60328
|
+
log
|
|
60329
|
+
})
|
|
60330
|
+
);
|
|
60331
|
+
ready("createTwzrdBeforePaymentHook");
|
|
60332
|
+
return { status: "composed", via: "createTwzrdBeforePaymentHook" };
|
|
60333
|
+
}
|
|
60334
|
+
if (typeof gate.installTwzrdAutoGate === "function") {
|
|
60335
|
+
gate.installTwzrdAutoGate(x402, installOpts);
|
|
60336
|
+
log.warn(
|
|
60337
|
+
"[ClawRouter] TWZRD AutoGate installed via installTwzrdAutoGate \u2014 that entry point registers its own hook, so the timeout budget does not apply and it replaces onBeforePaymentCreation on this client. Prefer a gate build exporting createTwzrdBeforePaymentHook."
|
|
60338
|
+
);
|
|
60339
|
+
ready("installTwzrdAutoGate");
|
|
60340
|
+
return { status: "composed", via: "installTwzrdAutoGate" };
|
|
60341
|
+
}
|
|
60342
|
+
throw new Error(
|
|
60343
|
+
`[ClawRouter] ${TWZRD_GATE_PACKAGE} is installed but exports neither installTwzrdAutoGate nor createTwzrdBeforePaymentHook`
|
|
60344
|
+
);
|
|
60345
|
+
}
|
|
60346
|
+
var TWZRD_GATE_PACKAGE, TRUTHY, FALSY, TWZRD_GATE_TIMEOUT_MS, TIMED_OUT;
|
|
60347
|
+
var init_twzrd_autogate = __esm({
|
|
60348
|
+
"src/twzrd-autogate.ts"() {
|
|
60349
|
+
"use strict";
|
|
60350
|
+
init_version4();
|
|
60351
|
+
TWZRD_GATE_PACKAGE = "twzrd-x402-gate";
|
|
60352
|
+
TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
60353
|
+
FALSY = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
|
|
60354
|
+
TWZRD_GATE_TIMEOUT_MS = 2e3;
|
|
60355
|
+
TIMED_OUT = { __twzrdTimedOut: true };
|
|
60356
|
+
}
|
|
60357
|
+
});
|
|
60358
|
+
|
|
60186
60359
|
// src/compression/types.ts
|
|
60187
60360
|
var DEFAULT_COMPRESSION_CONFIG;
|
|
60188
60361
|
var init_types2 = __esm({
|
|
@@ -91767,6 +91940,7 @@ async function startProxy(options) {
|
|
|
91767
91940
|
const evmSigner = toClientEvmSigner(account, evmPublicClient);
|
|
91768
91941
|
const spendControl = options.spendControl ?? getSharedSpendControl();
|
|
91769
91942
|
registerSpendPolicyHook(x402, spendControl);
|
|
91943
|
+
await maybeComposeTwzrdAutoGate(x402);
|
|
91770
91944
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
91771
91945
|
}
|
|
91772
91946
|
let solanaAddress;
|
|
@@ -95352,6 +95526,7 @@ var init_proxy = __esm({
|
|
|
95352
95526
|
init_auth();
|
|
95353
95527
|
init_api_key();
|
|
95354
95528
|
init_spend_control();
|
|
95529
|
+
init_twzrd_autogate();
|
|
95355
95530
|
init_compression();
|
|
95356
95531
|
init_errors5();
|
|
95357
95532
|
init_version4();
|
|
@@ -228140,7 +228315,7 @@ import {
|
|
|
228140
228315
|
} from "fs";
|
|
228141
228316
|
import { readFile as readFileAsync } from "fs/promises";
|
|
228142
228317
|
import { homedir as homedir14 } from "os";
|
|
228143
|
-
import { randomUUID } from "crypto";
|
|
228318
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
228144
228319
|
import { join as join17 } from "path";
|
|
228145
228320
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
228146
228321
|
const start = Date.now();
|
|
@@ -228155,7 +228330,7 @@ async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
|
228155
228330
|
return false;
|
|
228156
228331
|
}
|
|
228157
228332
|
function writePrivateJsonSync(path5, value) {
|
|
228158
|
-
const temporary = `${path5}.tmp.${
|
|
228333
|
+
const temporary = `${path5}.tmp.${randomUUID2()}`;
|
|
228159
228334
|
try {
|
|
228160
228335
|
writeFileSync3(temporary, JSON.stringify(value, null, 2), { mode: 384 });
|
|
228161
228336
|
renameSync2(temporary, path5);
|
package/docs/configuration.md
CHANGED
|
@@ -15,6 +15,7 @@ Complete reference for ClawRouter configuration options.
|
|
|
15
15
|
- [Tier Overrides](#tier-overrides)
|
|
16
16
|
- [Scoring Weights](#scoring-weights)
|
|
17
17
|
- [Spend Control & Counterparty Policy](#spend-control--counterparty-policy)
|
|
18
|
+
- [TWZRD AutoGate (opt-in)](#twzrd-autogate-opt-in)
|
|
18
19
|
- [Testing Configuration](#testing-configuration)
|
|
19
20
|
|
|
20
21
|
---
|
|
@@ -32,6 +33,9 @@ Complete reference for ClawRouter configuration options.
|
|
|
32
33
|
| `CLAWROUTER_WORKER` | - | Set to `1` to enable Worker Mode (earn USDC by running health checks). |
|
|
33
34
|
| `CLAWROUTER_DEBUG_HEADERS` | (on) | Set to `off`/`false`/`0` to suppress the `x-clawrouter-*` debug response headers. |
|
|
34
35
|
| `BLOCKRUN_WEB_SEARCH` | (auto-enabled) | Set to `off` to disable BlockRun's Exa web search provider registration. |
|
|
36
|
+
| `TWZRD_AUTO_GATE` | unset (off) | Set to `1` to compose TWZRD AutoGate after SpendControl on the x402 pre-sign hook. Also `TWZRD_GATE_ENABLED=true`. |
|
|
37
|
+
| `TWZRD_GATE_TIMEOUT_MS` | `2000` | How long the gate may take to answer before the payment proceeds on its own. Only meaningful with the gate on. |
|
|
38
|
+
| `TWZRD_FAIL_OPEN` | `true` | Set to `false` to refuse payments when the gate is unreachable instead of proceeding. Only meaningful with the gate on. |
|
|
35
39
|
|
|
36
40
|
---
|
|
37
41
|
|
|
@@ -732,6 +736,66 @@ placement, `clawrouter doctor`'s probe) are not covered.
|
|
|
732
736
|
|
|
733
737
|
---
|
|
734
738
|
|
|
739
|
+
## TWZRD AutoGate (opt-in)
|
|
740
|
+
|
|
741
|
+
Default **off**. This is not a re-open of default-on vendor lock. SpendControl
|
|
742
|
+
above remains the vendor-neutral path; TWZRD is composed only when you ask.
|
|
743
|
+
|
|
744
|
+
```bash
|
|
745
|
+
export TWZRD_AUTO_GATE=1
|
|
746
|
+
# equivalent:
|
|
747
|
+
export TWZRD_GATE_ENABLED=true
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
When set, `startProxy` registers the gate's `createTwzrdBeforePaymentHook` on the
|
|
751
|
+
**same** x402 client, **after** `registerSpendPolicyHook`. SpendControl lists
|
|
752
|
+
still run first. A wash `payTo` is refused before `signTransaction`.
|
|
753
|
+
|
|
754
|
+
### What it actually covers
|
|
755
|
+
|
|
756
|
+
**Solana, not Base.** The gate's reputation corpus is Solana. `classifyNetwork`
|
|
757
|
+
returns `network_not_scored` for `base` / `eip155:*`, and under our
|
|
758
|
+
`unsupportedNetworkMode: "observe"` those are waved through with verdict
|
|
759
|
+
`unknown` and no network call. The x402 client is shared between both chains, so
|
|
760
|
+
the hook is registered once and applies to Solana payments; `refuseWashFlagged`
|
|
761
|
+
can only ever fire there.
|
|
762
|
+
|
|
763
|
+
### What leaves your machine
|
|
764
|
+
|
|
765
|
+
On a Solana payment the gate POSTs the resource URL, `payTo`, price and chain to
|
|
766
|
+
`https://intel.twzrd.xyz/v1/intel/preflight`, stamped with
|
|
767
|
+
`X-TWZRD-Integration: clawrouter/<version>` and a per-process run id. Nothing is
|
|
768
|
+
sent when the flag is unset, and nothing is sent for Base.
|
|
769
|
+
|
|
770
|
+
### Availability
|
|
771
|
+
|
|
772
|
+
The gate's own preflight sets no timeout, and the package defaults to
|
|
773
|
+
`failOpen: false` — an outage there would refuse every paid Solana call. That is
|
|
774
|
+
the shape of the v0.12.271 outage, where an unreachable third party made every
|
|
775
|
+
Solana payment fail with a bare `fetch failed`. So:
|
|
776
|
+
|
|
777
|
+
- We bound the answer at `TWZRD_GATE_TIMEOUT_MS` (default `2000`).
|
|
778
|
+
- We pass `failOpen: true`. On timeout, rejection, or outage the payment
|
|
779
|
+
proceeds and a warning names the reason. SpendControl still applies — it is
|
|
780
|
+
the vendor-neutral guarantee and is untouched by any of this.
|
|
781
|
+
- `TWZRD_FAIL_OPEN=false` restores refuse-on-outage if you would rather stop
|
|
782
|
+
paying than pay unscored.
|
|
783
|
+
|
|
784
|
+
### Other notes
|
|
785
|
+
|
|
786
|
+
- Optional dependency: `twzrd-x402-gate@0.9.3`. Forks that omit it still install.
|
|
787
|
+
- Missing gate package (`MODULE_NOT_FOUND` for `twzrd-x402-gate` itself): fail
|
|
788
|
+
open — proxy boots, payments unguarded by TWZRD. Any other load/install error
|
|
789
|
+
fails closed.
|
|
790
|
+
- We register the hook ourselves rather than calling `installTwzrdAutoGate`,
|
|
791
|
+
which replaces `onBeforePaymentCreation` on the client — that would make every
|
|
792
|
+
hook registered afterwards skippable via `TWZRD_AUTO_GATE=0`, including ours.
|
|
793
|
+
- Identity: preflight stamps `X-Twzrd-Caller: clawrouter/<version>` so a refuse
|
|
794
|
+
is attributable.
|
|
795
|
+
- Unset the flag to return to SpendControl only.
|
|
796
|
+
|
|
797
|
+
---
|
|
798
|
+
|
|
735
799
|
## Testing Configuration
|
|
736
800
|
|
|
737
801
|
### Dry Run (No Payments)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/clawrouter",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.277",
|
|
4
4
|
"description": "Smart LLM router — save 84% on inference costs. 76 models (7 free). Pay by credit card (API key) or USDC (x402 wallet).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -106,6 +106,9 @@
|
|
|
106
106
|
"undici": "^8.10.0",
|
|
107
107
|
"viem": "^2.39.3"
|
|
108
108
|
},
|
|
109
|
+
"optionalDependencies": {
|
|
110
|
+
"twzrd-x402-gate": "0.9.3"
|
|
111
|
+
},
|
|
109
112
|
"peerDependencies": {
|
|
110
113
|
"openclaw": ">=2025.1.0"
|
|
111
114
|
},
|