solverr-agent 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +197 -2
- package/dist/agent-wallet.d.ts +64 -0
- package/dist/agent-wallet.js +194 -0
- package/dist/assets.d.ts +13 -0
- package/dist/assets.js +12 -0
- package/dist/chains/base.d.ts +24 -0
- package/dist/chains/base.js +96 -0
- package/dist/chains/chain-wallet.d.ts +15 -0
- package/dist/chains/chain-wallet.js +1 -0
- package/dist/chains/near.d.ts +28 -0
- package/dist/chains/near.js +96 -0
- package/dist/challenge.d.ts +11 -0
- package/dist/challenge.js +75 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +43 -0
- package/dist/coin-selection.d.ts +29 -0
- package/dist/coin-selection.js +60 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.js +33 -0
- package/dist/env.d.ts +4 -0
- package/dist/env.js +49 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +17 -0
- package/dist/gateway.d.ts +43 -0
- package/dist/gateway.js +24 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +5 -0
- package/dist/limits.d.ts +18 -0
- package/dist/limits.js +31 -0
- package/dist/mcp-server.d.ts +13 -0
- package/dist/mcp-server.js +153 -0
- package/dist/payer.d.ts +28 -0
- package/dist/payer.js +157 -0
- package/dist/spend-log.d.ts +38 -0
- package/dist/spend-log.js +162 -0
- package/dist/timeout.d.ts +7 -0
- package/dist/timeout.js +16 -0
- package/dist/url-guard.d.ts +7 -0
- package/dist/url-guard.js +92 -0
- package/package.json +63 -4
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { lookup } from "node:dns/promises";
|
|
2
|
+
import { BlockList, isIP } from "node:net";
|
|
3
|
+
import { Agent } from "undici";
|
|
4
|
+
import { PaymentError } from "./errors.js";
|
|
5
|
+
import { isHostAllowed } from "./limits.js";
|
|
6
|
+
/** Loopback, private, shared, link-local (cloud metadata), multicast and reserved ranges. IPv4-mapped IPv6 addresses match the IPv4 ranges. */
|
|
7
|
+
const PRIVATE_RANGES = new BlockList();
|
|
8
|
+
const IPV4_RANGES = [
|
|
9
|
+
["0.0.0.0", 8],
|
|
10
|
+
["10.0.0.0", 8],
|
|
11
|
+
["100.64.0.0", 10],
|
|
12
|
+
["127.0.0.0", 8],
|
|
13
|
+
["169.254.0.0", 16],
|
|
14
|
+
["172.16.0.0", 12],
|
|
15
|
+
["192.0.0.0", 24],
|
|
16
|
+
["192.168.0.0", 16],
|
|
17
|
+
["198.18.0.0", 15],
|
|
18
|
+
["224.0.0.0", 4],
|
|
19
|
+
["240.0.0.0", 4],
|
|
20
|
+
];
|
|
21
|
+
const IPV6_RANGES = [
|
|
22
|
+
["::", 96],
|
|
23
|
+
["100::", 64],
|
|
24
|
+
["2001::", 32],
|
|
25
|
+
["2002::", 16],
|
|
26
|
+
["fc00::", 7],
|
|
27
|
+
["fe80::", 10],
|
|
28
|
+
["fec0::", 10],
|
|
29
|
+
["ff00::", 8],
|
|
30
|
+
];
|
|
31
|
+
for (const [network, prefix] of IPV4_RANGES)
|
|
32
|
+
PRIVATE_RANGES.addSubnet(network, prefix, "ipv4");
|
|
33
|
+
for (const [network, prefix] of IPV6_RANGES)
|
|
34
|
+
PRIVATE_RANGES.addSubnet(network, prefix, "ipv6");
|
|
35
|
+
/** NAT64 prefix (`64:ff9b::/96`) whose last 32 bits are an IPv4 address. */
|
|
36
|
+
const NAT64_PREFIX = "64:ff9b::";
|
|
37
|
+
/** True for an address in a private or reserved range, including an IPv4 address inside the NAT64 prefix. Unparsable input counts as private. */
|
|
38
|
+
function isPrivateAddress(address) {
|
|
39
|
+
try {
|
|
40
|
+
const family = isIP(address);
|
|
41
|
+
if (family === 4)
|
|
42
|
+
return PRIVATE_RANGES.check(address, "ipv4");
|
|
43
|
+
if (family !== 6)
|
|
44
|
+
return true;
|
|
45
|
+
if (PRIVATE_RANGES.check(address, "ipv6"))
|
|
46
|
+
return true;
|
|
47
|
+
const normalized = new URL(`http://[${address}]`).hostname.slice(1, -1);
|
|
48
|
+
if (!normalized.startsWith(NAT64_PREFIX))
|
|
49
|
+
return false;
|
|
50
|
+
const tail = normalized.slice(NAT64_PREFIX.length).split(":").filter(Boolean).map((hextet) => parseInt(hextet, 16));
|
|
51
|
+
while (tail.length < 2)
|
|
52
|
+
tail.unshift(0);
|
|
53
|
+
const [high, low] = tail;
|
|
54
|
+
return PRIVATE_RANGES.check(`${high >> 8}.${high & 255}.${low >> 8}.${low & 255}`, "ipv4");
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Refuses a host off the allow list or resolving to any private address, unless the URL names that IP or `localhost` and it is allowed exactly.
|
|
61
|
+
Returns the checked public addresses for `pinnedDispatcher`; empty for an IP or `localhost` allowed exactly, which needs no pinning. */
|
|
62
|
+
export async function assertFetchAllowed(url, allow) {
|
|
63
|
+
const host = url.hostname.toLowerCase();
|
|
64
|
+
if (!isHostAllowed(host, allow)) {
|
|
65
|
+
throw new PaymentError("host_not_allowed", `Requests to ${host} are not allowed. Add it to the allow list to use this site.`);
|
|
66
|
+
}
|
|
67
|
+
const literal = host.replace(/^\[|\]$/g, "");
|
|
68
|
+
if ((isIP(literal) || host === "localhost") && allow.includes(host))
|
|
69
|
+
return [];
|
|
70
|
+
const addresses = isIP(literal) ? [literal] : (await lookup(literal, { all: true, verbatim: true })).map((entry) => entry.address);
|
|
71
|
+
const blocked = addresses.find(isPrivateAddress);
|
|
72
|
+
if (blocked) {
|
|
73
|
+
throw new PaymentError("host_not_allowed", `${host} resolves to the private address ${blocked}. Only public addresses can be requested.`);
|
|
74
|
+
}
|
|
75
|
+
return addresses;
|
|
76
|
+
}
|
|
77
|
+
/** Dispatcher that connects only to `addresses`, so a DNS answer changed after `assertFetchAllowed` cannot reach a private network.
|
|
78
|
+
Undefined when there is nothing to pin; close it once the response body is read. */
|
|
79
|
+
export function pinnedDispatcher(addresses) {
|
|
80
|
+
if (addresses.length === 0)
|
|
81
|
+
return undefined;
|
|
82
|
+
return new Agent({
|
|
83
|
+
connect: {
|
|
84
|
+
lookup: (_hostname, options, callback) => {
|
|
85
|
+
if (options.all)
|
|
86
|
+
callback(null, addresses.map((address) => ({ address, family: isIP(address) })));
|
|
87
|
+
else
|
|
88
|
+
callback(null, addresses[0], isIP(addresses[0]));
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solverr-agent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Agent wallet that pays x402 Solverr paywalls from NEAR and Base within spending limits, plus a local stdio MCP server.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/forcequitx/solverr/tree/main/packages/agent#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/forcequitx/solverr.git",
|
|
10
|
+
"directory": "packages/agent"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/forcequitx/solverr/issues"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"solverr-agent": "./dist/cli.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.22.2"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"x402",
|
|
39
|
+
"solverr",
|
|
40
|
+
"near-intents",
|
|
41
|
+
"ai-agents",
|
|
42
|
+
"mcp",
|
|
43
|
+
"payments"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.json",
|
|
47
|
+
"prepack": "npm run build",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
|
+
"test": "vitest run"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"solverr-x402": "0.1.1",
|
|
53
|
+
"@modelcontextprotocol/server": "2.4.0",
|
|
54
|
+
"near-api-js": "7.4.0",
|
|
55
|
+
"undici": "8.12.4",
|
|
56
|
+
"viem": "2.57.16",
|
|
57
|
+
"zod": "4.6.7"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "24.19.2",
|
|
61
|
+
"typescript": "6.0.3",
|
|
62
|
+
"vite": "8.3.7",
|
|
63
|
+
"vitest": "5.1.0"
|
|
64
|
+
}
|
|
65
|
+
}
|