x402-surface-check 0.2.16 → 0.2.17
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 +4 -1
- package/bin/x402-surface-check.mjs +55 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ npm: https://www.npmjs.com/package/x402-surface-check
|
|
|
10
10
|
npx --yes x402-surface-check https://api.example.com/.well-known/x402
|
|
11
11
|
npx --yes x402-surface-check https://api.example.com/openapi.json report.md
|
|
12
12
|
npx --yes x402-surface-check --endpoint --method POST https://x402.rpc.ankr.com/eth
|
|
13
|
+
npx --yes x402-surface-check --endpoint --method POST --body '{"prompt":"price CPI"}' https://api.example.com/paid-post
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
## What It Checks
|
|
@@ -17,7 +18,7 @@ npx --yes x402-surface-check --endpoint --method POST https://x402.rpc.ankr.com/
|
|
|
17
18
|
- Manifest endpoint discovery from `items[]`, `endpoints[]`, `resources[]`, `x402Endpoints`, category arrays, resource strings, and OpenAPI paths
|
|
18
19
|
- Linked discovery documents via `discovery_url`, `discoveryUrl`, `resources_url`, `resourcesUrl`, or manifest-level OpenAPI links
|
|
19
20
|
- OpenAPI `servers[]` base-path preservation, so `/paths` are probed through the documented gateway rather than the domain root
|
|
20
|
-
- OpenAPI query/path examples, JSON request-body examples,
|
|
21
|
+
- OpenAPI query/path examples, JSON request-body examples, nested request schemas, local `$ref` request schemas, and explicit direct-endpoint bodies for safer no-payment probes
|
|
21
22
|
- OpenAPI paid-operation prioritization, so docs and discovery routes do not consume the probe limit before payment-bearing operations
|
|
22
23
|
- No-payment HTTP 402 challenge shape
|
|
23
24
|
- x402 v1 and v2 price fields, including `accepts[]` and `schemes[]` challenge arrays
|
|
@@ -58,6 +59,8 @@ x402-surface-check --endpoint --method POST <paid-endpoint-url> [output.md]
|
|
|
58
59
|
|
|
59
60
|
--endpoint Treat the URL as one paid endpoint instead of a discovery document
|
|
60
61
|
--method <verb> HTTP method for direct endpoint mode, default POST
|
|
62
|
+
--body <json> JSON request body for direct endpoint mode
|
|
63
|
+
--body-file <p> Read JSON request body for direct endpoint mode from a file
|
|
61
64
|
--origin <url> Origin to use for browser-style CORS preflight
|
|
62
65
|
--limit <n> Maximum endpoints to probe, default 6
|
|
63
66
|
--json Print JSON instead of Markdown
|
|
@@ -18,6 +18,8 @@ Usage:
|
|
|
18
18
|
Options:
|
|
19
19
|
--endpoint Treat the URL as one paid endpoint instead of a discovery document
|
|
20
20
|
--method <verb> HTTP method for direct endpoint mode, default POST
|
|
21
|
+
--body <json> JSON request body for direct endpoint mode
|
|
22
|
+
--body-file <p> Read JSON request body for direct endpoint mode from a file
|
|
21
23
|
--origin <url> Origin to use for browser-style CORS preflight
|
|
22
24
|
--limit <n> Maximum endpoints to probe, default ${defaultLimit}
|
|
23
25
|
--json Print JSON instead of Markdown
|
|
@@ -33,6 +35,8 @@ function parseArgs(argv) {
|
|
|
33
35
|
limit: Number(process.env.X402_CHECK_LIMIT ?? defaultLimit),
|
|
34
36
|
method: 'POST',
|
|
35
37
|
origin: process.env.X402_CHECK_ORIGIN,
|
|
38
|
+
body: process.env.X402_CHECK_BODY,
|
|
39
|
+
bodyFile: process.env.X402_CHECK_BODY_FILE,
|
|
36
40
|
outputPath: '',
|
|
37
41
|
url: '',
|
|
38
42
|
}
|
|
@@ -55,6 +59,14 @@ function parseArgs(argv) {
|
|
|
55
59
|
args.method = String(argv[index + 1] ?? '').toUpperCase()
|
|
56
60
|
index += 1
|
|
57
61
|
}
|
|
62
|
+
else if (arg === '--body') {
|
|
63
|
+
args.body = argv[index + 1]
|
|
64
|
+
index += 1
|
|
65
|
+
}
|
|
66
|
+
else if (arg === '--body-file') {
|
|
67
|
+
args.bodyFile = argv[index + 1]
|
|
68
|
+
index += 1
|
|
69
|
+
}
|
|
58
70
|
else if (arg === '--origin') {
|
|
59
71
|
args.origin = argv[index + 1]
|
|
60
72
|
index += 1
|
|
@@ -77,6 +89,23 @@ function parseArgs(argv) {
|
|
|
77
89
|
return args
|
|
78
90
|
}
|
|
79
91
|
|
|
92
|
+
async function directEndpointRequestBody(options) {
|
|
93
|
+
if (!options.endpoint) return undefined
|
|
94
|
+
if (options.body && options.bodyFile) {
|
|
95
|
+
throw new Error('Use either --body or --body-file, not both.')
|
|
96
|
+
}
|
|
97
|
+
const raw = options.bodyFile
|
|
98
|
+
? await readFile(options.bodyFile, 'utf8')
|
|
99
|
+
: options.body
|
|
100
|
+
if (!raw) return undefined
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(raw)
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new Error(`Request body must be valid JSON: ${error.message}`)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
80
109
|
function moneyFromAtomic(amount, decimals = 6) {
|
|
81
110
|
if (amount === '' || amount === null || amount === undefined) return ''
|
|
82
111
|
const numeric = Number(amount)
|
|
@@ -176,9 +205,13 @@ function resolveSchema(schema, document, seen = new Set()) {
|
|
|
176
205
|
return resolveSchema(resolved, document, seen)
|
|
177
206
|
}
|
|
178
207
|
|
|
179
|
-
function exampleValue(schemaOrParameter, document) {
|
|
208
|
+
function exampleValue(schemaOrParameter, document, depth = 0) {
|
|
180
209
|
if (!schemaOrParameter || typeof schemaOrParameter !== 'object') return undefined
|
|
181
210
|
const schema = resolveSchema(schemaOrParameter.schema ?? schemaOrParameter, document)
|
|
211
|
+
const composite = schema.oneOf ?? schema.anyOf ?? schema.allOf
|
|
212
|
+
if (Array.isArray(composite) && composite.length > 0) {
|
|
213
|
+
return exampleValue(composite[0], document, depth + 1)
|
|
214
|
+
}
|
|
182
215
|
const value = schemaOrParameter.example
|
|
183
216
|
?? schema.const
|
|
184
217
|
?? schema.example
|
|
@@ -195,6 +228,25 @@ function exampleValue(schemaOrParameter, document) {
|
|
|
195
228
|
if (schema.type === 'integer') return Number.isFinite(Number(schema.minimum)) ? Number(schema.minimum) : 1
|
|
196
229
|
if (schema.type === 'number') return Number.isFinite(Number(schema.minimum)) ? Number(schema.minimum) : 1
|
|
197
230
|
if (schema.type === 'boolean') return false
|
|
231
|
+
if (schema.type === 'array') {
|
|
232
|
+
if (depth > 4) return []
|
|
233
|
+
const item = exampleValue(schema.items ?? {}, document, depth + 1)
|
|
234
|
+
return item === undefined ? [] : [item]
|
|
235
|
+
}
|
|
236
|
+
if (schema.type === 'object') {
|
|
237
|
+
if (depth > 4) return {}
|
|
238
|
+
const properties = schema.properties && typeof schema.properties === 'object'
|
|
239
|
+
? schema.properties
|
|
240
|
+
: {}
|
|
241
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : Object.keys(properties))
|
|
242
|
+
const result = {}
|
|
243
|
+
for (const [key, property] of Object.entries(properties)) {
|
|
244
|
+
if (!required.has(key)) continue
|
|
245
|
+
const nestedValue = exampleValue(property, document, depth + 1)
|
|
246
|
+
if (nestedValue !== undefined) result[key] = nestedValue
|
|
247
|
+
}
|
|
248
|
+
return result
|
|
249
|
+
}
|
|
198
250
|
return undefined
|
|
199
251
|
}
|
|
200
252
|
|
|
@@ -812,6 +864,7 @@ function formatMarkdown(report) {
|
|
|
812
864
|
}
|
|
813
865
|
|
|
814
866
|
async function runCheck(options) {
|
|
867
|
+
const directRequestBody = await directEndpointRequestBody(options)
|
|
815
868
|
let sourceDocument = null
|
|
816
869
|
let document = options.endpoint
|
|
817
870
|
? {
|
|
@@ -823,7 +876,7 @@ async function runCheck(options) {
|
|
|
823
876
|
}
|
|
824
877
|
: await fetchDocument(options.url)
|
|
825
878
|
let entries = options.endpoint
|
|
826
|
-
? [{ name: new URL(options.url).pathname.split('/').filter(Boolean).at(-1) ?? options.url, url: options.url, method: options.method || 'POST' }]
|
|
879
|
+
? [{ name: new URL(options.url).pathname.split('/').filter(Boolean).at(-1) ?? options.url, url: options.url, method: options.method || 'POST', requestBody: directRequestBody }]
|
|
827
880
|
: (document.body.json ? endpointEntries(document.body.json, document.url, options.limit) : [])
|
|
828
881
|
|
|
829
882
|
if (!options.endpoint && entries.length === 0 && document.body.json) {
|