@agentcash/discovery 1.0.0 → 1.0.2
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 +86 -154
- package/dist/cli.cjs +53 -25
- package/dist/cli.js +53 -25
- package/dist/index.cjs +53 -25
- package/dist/index.js +53 -25
- package/dist/schemas.cjs +1 -0
- package/dist/schemas.d.cts +1 -0
- package/dist/schemas.d.ts +1 -0
- package/dist/schemas.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Canonical discovery runtime for the agentcash ecosystem.
|
|
4
4
|
|
|
5
|
-
Use one library for CLI,
|
|
5
|
+
Use one library for MCP, CLI, router, and audit so discovery behavior is identical everywhere.
|
|
6
6
|
|
|
7
7
|
## Why One Library
|
|
8
8
|
|
|
9
9
|
- Same parsing logic across surfaces: no CLI/server/client drift.
|
|
10
|
+
- Shared Zod schema that the router can test against at compile-time.
|
|
10
11
|
- Same warning codes and precedence rules: fewer integration surprises.
|
|
11
12
|
- Same compatibility adapters in one place: legacy behavior is isolated and removable.
|
|
12
|
-
- Same L0-L5 harness model: easier context-budget auditing and rollout decisions.
|
|
13
13
|
|
|
14
14
|
## L0-L5 Mental Model
|
|
15
15
|
|
|
@@ -22,196 +22,128 @@ Use one library for CLI, server, and client so discovery behavior is identical e
|
|
|
22
22
|
|
|
23
23
|
Design rule: `L0` + `L1` are zero-hop critical. `L2+` should be fetched on demand.
|
|
24
24
|
|
|
25
|
+
In practice, each layer should guide the agent to discover the next:
|
|
26
|
+
|
|
27
|
+
| Layer | Surface | What the agent gets |
|
|
28
|
+
| ------ | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| **L0** | MCP tool description / `agentcash --help` | First impression of agentcash. Should encourage the agent to use it and explicitly explain `discoverOriginSchema` and `checkEndpointSchema`. |
|
|
30
|
+
| **L1** | Same location as L0 | List of domains available to the agent. Each entry should be descriptive enough for the agent to understand what it does at a high level. |
|
|
31
|
+
| **L2** | `discoverOriginSchema` result | Detailed description of the origin and its supported endpoints. |
|
|
32
|
+
| **L3** | `checkEndpointSchema` result | Specific guidance for a single endpoint: input/output schema, auth mode, and a detailed description of what the endpoint does. |
|
|
33
|
+
| **L4** | `discoverOriginSchema` with `includeGuidance: true` | Composition guidance for 2+ resources at an origin. Sourced from the `guidance` field in OpenAPI. |
|
|
34
|
+
|
|
25
35
|
## Install
|
|
26
36
|
|
|
27
37
|
```bash
|
|
28
38
|
pnpm add @agentcash/discovery
|
|
29
39
|
```
|
|
30
40
|
|
|
31
|
-
## CLI
|
|
41
|
+
## CLI
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
Two commands: `discover` (list endpoints at an origin) and `check` (inspect a specific URL).
|
|
34
44
|
|
|
35
45
|
```bash
|
|
46
|
+
# Discover all endpoints at an origin
|
|
36
47
|
npx @agentcash/discovery stabletravel.dev
|
|
37
|
-
|
|
48
|
+
npx @agentcash/discovery discover stabletravel.dev
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npx @agentcash/discovery stabletravel.dev -v
|
|
50
|
+
# Inspect a specific endpoint URL
|
|
51
|
+
npx @agentcash/discovery check https://stabletravel.dev/search
|
|
43
52
|
```
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
Flags:
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
| Flag | Description |
|
|
57
|
+
| -------- | -------------------------------------------------- |
|
|
58
|
+
| `--json` | Machine-readable JSON output |
|
|
59
|
+
| `-v` | Verbose — includes guidance text and warning hints |
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
JSON output shape (`discover`):
|
|
52
62
|
|
|
53
|
-
```
|
|
54
|
-
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"ok": true,
|
|
66
|
+
"selectedStage": "openapi",
|
|
67
|
+
"resources": [{ "resourceKey": "GET /search", "method": "GET", "path": "/search" }],
|
|
68
|
+
"warnings": [],
|
|
69
|
+
"meta": { "origin": "https://stabletravel.dev", "specUrl": "..." }
|
|
70
|
+
}
|
|
55
71
|
```
|
|
56
72
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
|
|
73
|
+
JSON output shape (`check`):
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"url": "https://stabletravel.dev/search",
|
|
78
|
+
"found": true,
|
|
79
|
+
"origin": "https://stabletravel.dev",
|
|
80
|
+
"path": "/search",
|
|
81
|
+
"advisories": [{ "method": "GET", "authMode": "bearer", "estimatedPrice": "$0.01" }],
|
|
82
|
+
"warnings": []
|
|
83
|
+
}
|
|
61
84
|
```
|
|
62
85
|
|
|
63
|
-
Useful flags:
|
|
64
|
-
|
|
65
|
-
- `--compat on|off|strict`
|
|
66
|
-
- `--probe`
|
|
67
|
-
- `--timeout-ms <ms>`
|
|
68
|
-
- `--no-truncate`
|
|
69
|
-
- `--no-color`
|
|
70
|
-
|
|
71
86
|
## Programmatic Usage
|
|
72
87
|
|
|
73
88
|
```ts
|
|
74
|
-
import {
|
|
75
|
-
auditContextHarness,
|
|
76
|
-
discover,
|
|
77
|
-
discoverDetailed,
|
|
78
|
-
validatePaymentRequiredDetailed,
|
|
79
|
-
type HarnessClientId,
|
|
80
|
-
} from '@agentcash/discovery';
|
|
81
|
-
|
|
82
|
-
const progressive = await discover({
|
|
83
|
-
target: 'stabletravel.dev',
|
|
84
|
-
compatMode: 'on',
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const detailed = await discoverDetailed({
|
|
88
|
-
target: 'stabletravel.dev',
|
|
89
|
-
compatMode: 'strict',
|
|
90
|
-
rawView: 'full',
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const client: HarnessClientId = 'claude-code';
|
|
94
|
-
const harness = await auditContextHarness({
|
|
95
|
-
target: 'stabletravel.dev',
|
|
96
|
-
client,
|
|
97
|
-
contextWindowTokens: 200000,
|
|
98
|
-
compatMode: 'on',
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const validation = validatePaymentRequiredDetailed(payload, {
|
|
102
|
-
compatMode: 'strict',
|
|
103
|
-
metadata: {
|
|
104
|
-
title: 'Example API',
|
|
105
|
-
description: 'Sample description',
|
|
106
|
-
favicon: 'https://example.com/favicon.ico',
|
|
107
|
-
ogImages: [{ url: 'https://example.com/og.png' }],
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## MCP Adapter Contract
|
|
113
|
-
|
|
114
|
-
This section is the canonical contract for MCP-facing discovery adapters:
|
|
115
|
-
|
|
116
|
-
- `discoverForMcp(options)` for L2 resources + L4 guidance policy projection.
|
|
117
|
-
- `inspectEndpointForMcp(options)` for L3 OpenAPI advisory projection.
|
|
118
|
-
|
|
119
|
-
`discoverForMcp` guarantees:
|
|
120
|
-
|
|
121
|
-
- `guidanceAvailable` is always present.
|
|
122
|
-
- `guidanceTokens` is present when guidance exists.
|
|
123
|
-
- `guidance` is included when `includeGuidance=true`, excluded when
|
|
124
|
-
`includeGuidance=false`, and auto-included under the token threshold when not
|
|
125
|
-
specified.
|
|
126
|
-
|
|
127
|
-
`inspectEndpointForMcp` guarantees:
|
|
128
|
-
|
|
129
|
-
- Spec-derived HTTP methods for the selected endpoint path.
|
|
130
|
-
- Per-method advisory data: summary, estimated price, protocols, auth mode, and
|
|
131
|
-
input schema.
|
|
132
|
-
|
|
133
|
-
Ownership boundary:
|
|
134
|
-
|
|
135
|
-
- `@agentcash/discovery` owns discovery/advisory contracts.
|
|
136
|
-
- Runtime probe truth (live 402 parsing/payment option extraction/divergence)
|
|
137
|
-
belongs to the `agentcash` MCP package.
|
|
138
|
-
|
|
139
|
-
Reference integration boundary:
|
|
140
|
-
|
|
141
|
-
- `https://github.com/Merit-Systems/agentcash/blob/main/packages/external/mcp/docs/discovery-boundary.md`
|
|
89
|
+
import { discoverOriginSchema, checkEndpointSchema } from '@agentcash/discovery';
|
|
142
90
|
|
|
143
|
-
|
|
91
|
+
// Discover all endpoints at an origin
|
|
92
|
+
const result = await discoverOriginSchema({ target: 'stabletravel.dev' });
|
|
93
|
+
// result.found === true → result.endpoints (L2Route[]), result.guidance?, result.guidanceTokens?
|
|
144
94
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
3. `/.well-known/x402` (compat)
|
|
150
|
-
4. DNS TXT `_x402` pointers (compat)
|
|
151
|
-
5. Probe fallback (only when probe candidates are provided)
|
|
95
|
+
// Inspect a specific endpoint URL
|
|
96
|
+
const check = await checkEndpointSchema({ url: 'https://stabletravel.dev/search' });
|
|
97
|
+
// check.found === true → check.advisories (per-method: authMode, estimatedPrice, protocols, inputSchema)
|
|
98
|
+
```
|
|
152
99
|
|
|
153
|
-
|
|
100
|
+
## Exported API
|
|
154
101
|
|
|
155
|
-
|
|
156
|
-
- `discoverDetailed(...)` runs full waterfall and merges deterministically.
|
|
102
|
+
### Core discovery
|
|
157
103
|
|
|
158
|
-
|
|
104
|
+
| Export | Description |
|
|
105
|
+
| ------------------------ | --------------------------------------------------------- |
|
|
106
|
+
| `discoverOriginSchema()` | Progressive discovery — returns endpoints + advisory data |
|
|
107
|
+
| `checkEndpointSchema()` | Per-endpoint inspection — returns per-method advisories |
|
|
159
108
|
|
|
160
|
-
|
|
161
|
-
- Product policy diagnostics (network/schema/metadata) are layered on top with stable issue codes.
|
|
109
|
+
### Layer fetchers (low-level)
|
|
162
110
|
|
|
163
|
-
|
|
111
|
+
| Export | Layer | Description |
|
|
112
|
+
| -------------------------- | ----- | ------------------------------------- |
|
|
113
|
+
| `getOpenAPI(origin)` | — | Fetch OpenAPI spec from origin |
|
|
114
|
+
| `getWellKnown(origin)` | — | Fetch `/.well-known/x402` document |
|
|
115
|
+
| `getProbe(url, body?)` | — | Live endpoint probe |
|
|
116
|
+
| `checkL2ForOpenAPI(spec)` | L2 | Extract route list from OpenAPI |
|
|
117
|
+
| `checkL2ForWellknown(doc)` | L2 | Extract route list from well-known |
|
|
118
|
+
| `getL3(origin, path)` | L3 | Get detailed metadata for an endpoint |
|
|
119
|
+
| `checkL4ForOpenAPI(spec)` | L4 | Extract guidance from OpenAPI |
|
|
120
|
+
| `checkL4ForWellknown(doc)` | L4 | Extract guidance from well-known |
|
|
164
121
|
|
|
165
|
-
|
|
166
|
-
- `off`: canonical-only behavior.
|
|
167
|
-
- `strict`: legacy adapters enabled, selected warnings escalated.
|
|
122
|
+
### Validation
|
|
168
123
|
|
|
169
|
-
|
|
124
|
+
| Export | Description |
|
|
125
|
+
| ----------------------------------- | -------------------------------------------- |
|
|
126
|
+
| `validatePaymentRequiredDetailed()` | Full 402 payload validation with diagnostics |
|
|
127
|
+
| `evaluateMetadataCompleteness()` | Metadata quality score |
|
|
128
|
+
| `VALIDATION_CODES` | Stable issue code constants |
|
|
170
129
|
|
|
171
|
-
|
|
130
|
+
### Audit
|
|
172
131
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
132
|
+
| Export | Description |
|
|
133
|
+
| --------------------------- | ------------------------------ |
|
|
134
|
+
| `getWarningsForOpenAPI()` | Warnings for OpenAPI source |
|
|
135
|
+
| `getWarningsForWellKnown()` | Warnings for well-known source |
|
|
136
|
+
| `getWarningsForL2()` | Warnings for route list |
|
|
137
|
+
| `getWarningsForL3()` | Warnings for endpoint metadata |
|
|
138
|
+
| `getWarningsForL4()` | Warnings for guidance layer |
|
|
139
|
+
| `AUDIT_CODES` | Stable audit code constants |
|
|
176
140
|
|
|
177
|
-
|
|
141
|
+
Ownership boundary:
|
|
178
142
|
|
|
179
|
-
- `
|
|
180
|
-
- `
|
|
181
|
-
- `method`
|
|
182
|
-
- `path`
|
|
183
|
-
- `source`
|
|
184
|
-
- `verified` (default `false`)
|
|
143
|
+
- `@agentcash/discovery` owns discovery/advisory contracts.
|
|
144
|
+
- `@agentcash` should own all signing logic, but should be composable with the methods for probing built in this package.
|
|
185
145
|
|
|
186
146
|
Philosophy boundary:
|
|
187
147
|
|
|
188
148
|
- Machine-parsable discovery metadata belongs in OpenAPI.
|
|
189
|
-
- `llms.txt` is optional, unstructured guidance.
|
|
190
149
|
- Discovery is advisory. Runtime payment challenge/probe is authoritative.
|
|
191
|
-
|
|
192
|
-
## Internal Registry Audit Harness
|
|
193
|
-
|
|
194
|
-
For x402scan registry benchmarking:
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
SCAN_DATABASE_URL='postgresql://...' pnpm audit:registry
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Quick sample:
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
SCAN_DATABASE_URL='postgresql://...' pnpm audit:registry:quick
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Output:
|
|
207
|
-
|
|
208
|
-
- `audit/registry-audit-<timestamp>.json`
|
|
209
|
-
- `audit/registry-audit-latest.json`
|
|
210
|
-
|
|
211
|
-
## Deeper Docs
|
|
212
|
-
|
|
213
|
-
Architecture and planning artifacts live in `.claude/`.
|
|
214
|
-
|
|
215
|
-
Validation design doc:
|
|
216
|
-
|
|
217
|
-
- `docs/VALIDATION_DIAGNOSTICS_DESIGN_2026-03-03.md`
|
package/dist/cli.cjs
CHANGED
|
@@ -76,6 +76,7 @@ var OpenApiDocSchema = import_zod.z.object({
|
|
|
76
76
|
description: import_zod.z.string().optional(),
|
|
77
77
|
guidance: import_zod.z.string().optional()
|
|
78
78
|
}),
|
|
79
|
+
security: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.array(import_zod.z.string()))).optional(),
|
|
79
80
|
servers: import_zod.z.array(import_zod.z.object({ url: import_zod.z.string() })).optional(),
|
|
80
81
|
tags: import_zod.z.array(import_zod.z.object({ name: import_zod.z.string() })).optional(),
|
|
81
82
|
components: import_zod.z.object({ securitySchemes: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional() }).optional(),
|
|
@@ -105,20 +106,31 @@ var WellKnownParsedSchema = import_zod.z.object({
|
|
|
105
106
|
function isRecord(value) {
|
|
106
107
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
107
108
|
}
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
function resolveSecurityFlags(requirements, securitySchemes) {
|
|
110
|
+
let hasApiKey = false;
|
|
111
|
+
let hasSiwx = false;
|
|
112
|
+
for (const requirement of requirements) {
|
|
113
|
+
for (const schemeName of Object.keys(requirement)) {
|
|
114
|
+
if (schemeName === "siwx") {
|
|
115
|
+
hasSiwx = true;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (schemeName === "apiKey") {
|
|
119
|
+
hasApiKey = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const def = securitySchemes[schemeName];
|
|
123
|
+
if (isRecord(def) && def["type"] === "apiKey") hasApiKey = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { hasApiKey, hasSiwx };
|
|
113
127
|
}
|
|
114
|
-
function inferAuthMode(operation) {
|
|
128
|
+
function inferAuthMode(operation, globalSecurity, securitySchemes) {
|
|
115
129
|
const hasXPaymentInfo = Boolean(operation["x-payment-info"]);
|
|
116
|
-
const
|
|
117
|
-
const hasApiKey =
|
|
118
|
-
|
|
119
|
-
if (hasPayment && hasApiKey) return "apiKey+paid";
|
|
130
|
+
const effectiveSecurity = operation.security !== void 0 && operation.security.length > 0 ? operation.security : globalSecurity ?? [];
|
|
131
|
+
const { hasApiKey, hasSiwx } = resolveSecurityFlags(effectiveSecurity, securitySchemes ?? {});
|
|
132
|
+
if (hasXPaymentInfo && hasApiKey) return "apiKey+paid";
|
|
120
133
|
if (hasXPaymentInfo) return "paid";
|
|
121
|
-
if (hasPayment) return hasSiwx ? "siwx" : "paid";
|
|
122
134
|
if (hasApiKey) return "apiKey";
|
|
123
135
|
if (hasSiwx) return "siwx";
|
|
124
136
|
return void 0;
|
|
@@ -138,10 +150,12 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
|
138
150
|
var DEFAULT_MISSING_METHOD = "POST";
|
|
139
151
|
|
|
140
152
|
// src/core/lib/url.ts
|
|
141
|
-
function
|
|
153
|
+
function ensureProtocol(target) {
|
|
142
154
|
const trimmed = target.trim();
|
|
143
|
-
|
|
144
|
-
|
|
155
|
+
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
156
|
+
}
|
|
157
|
+
function normalizeOrigin(target) {
|
|
158
|
+
const url = new URL(ensureProtocol(target));
|
|
145
159
|
url.pathname = "";
|
|
146
160
|
url.search = "";
|
|
147
161
|
url.hash = "";
|
|
@@ -180,7 +194,7 @@ function fetchSafe(url, init) {
|
|
|
180
194
|
}
|
|
181
195
|
|
|
182
196
|
// src/mmm-enabled.ts
|
|
183
|
-
var isMmmEnabled = () => "1.0.
|
|
197
|
+
var isMmmEnabled = () => "1.0.2".includes("-mmm");
|
|
184
198
|
|
|
185
199
|
// src/core/source/openapi/index.ts
|
|
186
200
|
var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
@@ -189,15 +203,13 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
|
189
203
|
for (const httpMethod of [...HTTP_METHODS]) {
|
|
190
204
|
const operation = pathItem[httpMethod.toLowerCase()];
|
|
191
205
|
if (!operation) continue;
|
|
192
|
-
const authMode = inferAuthMode(operation) ?? void 0;
|
|
206
|
+
const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
|
|
193
207
|
if (!authMode) continue;
|
|
194
208
|
const p = operation["x-payment-info"];
|
|
195
209
|
const protocols = (p?.protocols ?? []).filter(
|
|
196
210
|
(proto) => proto !== "mpp" || isMmmEnabled()
|
|
197
211
|
);
|
|
198
|
-
|
|
199
|
-
if (authMode === "paid" && protocols.length === 0) continue;
|
|
200
|
-
const pricing = authMode === "paid" && p ? {
|
|
212
|
+
const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
|
|
201
213
|
pricingMode: p.pricingMode,
|
|
202
214
|
...p.price ? { price: p.price } : {},
|
|
203
215
|
...p.minPrice ? { minPrice: p.minPrice } : {},
|
|
@@ -643,10 +655,12 @@ function extractSchemas3(payload) {
|
|
|
643
655
|
return {};
|
|
644
656
|
}
|
|
645
657
|
function parseInputSchema(payload) {
|
|
646
|
-
|
|
658
|
+
const schema = extractSchemas3(payload).inputSchema;
|
|
659
|
+
return schema;
|
|
647
660
|
}
|
|
648
661
|
function parseOutputSchema(payload) {
|
|
649
|
-
|
|
662
|
+
const schema = extractSchemas3(payload).outputSchema;
|
|
663
|
+
return schema;
|
|
650
664
|
}
|
|
651
665
|
|
|
652
666
|
// src/core/protocols/x402/v1/parse-payment-required.ts
|
|
@@ -874,6 +888,20 @@ function extractRequestBodySchema(operationSchema) {
|
|
|
874
888
|
}
|
|
875
889
|
return void 0;
|
|
876
890
|
}
|
|
891
|
+
function extractOutputSchema(operationSchema) {
|
|
892
|
+
const responses = operationSchema.responses;
|
|
893
|
+
if (!isRecord(responses)) return void 0;
|
|
894
|
+
const candidate = responses["200"] ?? responses["201"] ?? Object.entries(responses).find(([k]) => k.startsWith("2"))?.[1];
|
|
895
|
+
if (!isRecord(candidate)) return void 0;
|
|
896
|
+
const content = candidate.content;
|
|
897
|
+
if (!isRecord(content)) return void 0;
|
|
898
|
+
const json = content["application/json"];
|
|
899
|
+
if (isRecord(json) && isRecord(json.schema)) return json.schema;
|
|
900
|
+
for (const mediaType of Object.values(content)) {
|
|
901
|
+
if (isRecord(mediaType) && isRecord(mediaType.schema)) return mediaType.schema;
|
|
902
|
+
}
|
|
903
|
+
return void 0;
|
|
904
|
+
}
|
|
877
905
|
function extractParameters(operationSchema) {
|
|
878
906
|
const params = operationSchema.parameters;
|
|
879
907
|
if (!Array.isArray(params)) return [];
|
|
@@ -932,7 +960,7 @@ function getL3ForOpenAPI(openApi, path, method) {
|
|
|
932
960
|
estimatedPrice: parseOperationPrice(resolvedOperation),
|
|
933
961
|
protocols: parseOperationProtocols(resolvedOperation),
|
|
934
962
|
inputSchema: extractInputSchema(resolvedOperation),
|
|
935
|
-
outputSchema: resolvedOperation
|
|
963
|
+
outputSchema: extractOutputSchema(resolvedOperation)
|
|
936
964
|
};
|
|
937
965
|
}
|
|
938
966
|
function getL3ForProbe(probe, path, method) {
|
|
@@ -969,12 +997,12 @@ function getAdvisoriesForProbe(probe, path) {
|
|
|
969
997
|
});
|
|
970
998
|
}
|
|
971
999
|
async function checkEndpointSchema(options) {
|
|
972
|
-
const endpoint = new URL(options.url);
|
|
1000
|
+
const endpoint = new URL(ensureProtocol(options.url));
|
|
973
1001
|
const origin = normalizeOrigin(endpoint.origin);
|
|
974
1002
|
const path = normalizePath(endpoint.pathname || "/");
|
|
975
1003
|
if (options.sampleInputBody !== void 0) {
|
|
976
1004
|
const probeResult2 = await getProbe(
|
|
977
|
-
|
|
1005
|
+
endpoint.href,
|
|
978
1006
|
options.headers,
|
|
979
1007
|
options.signal,
|
|
980
1008
|
options.sampleInputBody
|
|
@@ -999,7 +1027,7 @@ async function checkEndpointSchema(options) {
|
|
|
999
1027
|
if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
|
|
1000
1028
|
return { found: false, origin, path, cause: "not_found" };
|
|
1001
1029
|
}
|
|
1002
|
-
const probeResult = await getProbe(
|
|
1030
|
+
const probeResult = await getProbe(endpoint.href, options.headers, options.signal);
|
|
1003
1031
|
if (probeResult.isErr()) {
|
|
1004
1032
|
return {
|
|
1005
1033
|
found: false,
|
package/dist/cli.js
CHANGED
|
@@ -50,6 +50,7 @@ var OpenApiDocSchema = z.object({
|
|
|
50
50
|
description: z.string().optional(),
|
|
51
51
|
guidance: z.string().optional()
|
|
52
52
|
}),
|
|
53
|
+
security: z.array(z.record(z.string(), z.array(z.string()))).optional(),
|
|
53
54
|
servers: z.array(z.object({ url: z.string() })).optional(),
|
|
54
55
|
tags: z.array(z.object({ name: z.string() })).optional(),
|
|
55
56
|
components: z.object({ securitySchemes: z.record(z.string(), z.unknown()).optional() }).optional(),
|
|
@@ -79,20 +80,31 @@ var WellKnownParsedSchema = z.object({
|
|
|
79
80
|
function isRecord(value) {
|
|
80
81
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
function resolveSecurityFlags(requirements, securitySchemes) {
|
|
84
|
+
let hasApiKey = false;
|
|
85
|
+
let hasSiwx = false;
|
|
86
|
+
for (const requirement of requirements) {
|
|
87
|
+
for (const schemeName of Object.keys(requirement)) {
|
|
88
|
+
if (schemeName === "siwx") {
|
|
89
|
+
hasSiwx = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (schemeName === "apiKey") {
|
|
93
|
+
hasApiKey = true;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const def = securitySchemes[schemeName];
|
|
97
|
+
if (isRecord(def) && def["type"] === "apiKey") hasApiKey = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { hasApiKey, hasSiwx };
|
|
87
101
|
}
|
|
88
|
-
function inferAuthMode(operation) {
|
|
102
|
+
function inferAuthMode(operation, globalSecurity, securitySchemes) {
|
|
89
103
|
const hasXPaymentInfo = Boolean(operation["x-payment-info"]);
|
|
90
|
-
const
|
|
91
|
-
const hasApiKey =
|
|
92
|
-
|
|
93
|
-
if (hasPayment && hasApiKey) return "apiKey+paid";
|
|
104
|
+
const effectiveSecurity = operation.security !== void 0 && operation.security.length > 0 ? operation.security : globalSecurity ?? [];
|
|
105
|
+
const { hasApiKey, hasSiwx } = resolveSecurityFlags(effectiveSecurity, securitySchemes ?? {});
|
|
106
|
+
if (hasXPaymentInfo && hasApiKey) return "apiKey+paid";
|
|
94
107
|
if (hasXPaymentInfo) return "paid";
|
|
95
|
-
if (hasPayment) return hasSiwx ? "siwx" : "paid";
|
|
96
108
|
if (hasApiKey) return "apiKey";
|
|
97
109
|
if (hasSiwx) return "siwx";
|
|
98
110
|
return void 0;
|
|
@@ -112,10 +124,12 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
|
112
124
|
var DEFAULT_MISSING_METHOD = "POST";
|
|
113
125
|
|
|
114
126
|
// src/core/lib/url.ts
|
|
115
|
-
function
|
|
127
|
+
function ensureProtocol(target) {
|
|
116
128
|
const trimmed = target.trim();
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
130
|
+
}
|
|
131
|
+
function normalizeOrigin(target) {
|
|
132
|
+
const url = new URL(ensureProtocol(target));
|
|
119
133
|
url.pathname = "";
|
|
120
134
|
url.search = "";
|
|
121
135
|
url.hash = "";
|
|
@@ -154,7 +168,7 @@ function fetchSafe(url, init) {
|
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
// src/mmm-enabled.ts
|
|
157
|
-
var isMmmEnabled = () => "1.0.
|
|
171
|
+
var isMmmEnabled = () => "1.0.2".includes("-mmm");
|
|
158
172
|
|
|
159
173
|
// src/core/source/openapi/index.ts
|
|
160
174
|
var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
@@ -163,15 +177,13 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
|
163
177
|
for (const httpMethod of [...HTTP_METHODS]) {
|
|
164
178
|
const operation = pathItem[httpMethod.toLowerCase()];
|
|
165
179
|
if (!operation) continue;
|
|
166
|
-
const authMode = inferAuthMode(operation) ?? void 0;
|
|
180
|
+
const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
|
|
167
181
|
if (!authMode) continue;
|
|
168
182
|
const p = operation["x-payment-info"];
|
|
169
183
|
const protocols = (p?.protocols ?? []).filter(
|
|
170
184
|
(proto) => proto !== "mpp" || isMmmEnabled()
|
|
171
185
|
);
|
|
172
|
-
|
|
173
|
-
if (authMode === "paid" && protocols.length === 0) continue;
|
|
174
|
-
const pricing = authMode === "paid" && p ? {
|
|
186
|
+
const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
|
|
175
187
|
pricingMode: p.pricingMode,
|
|
176
188
|
...p.price ? { price: p.price } : {},
|
|
177
189
|
...p.minPrice ? { minPrice: p.minPrice } : {},
|
|
@@ -617,10 +629,12 @@ function extractSchemas3(payload) {
|
|
|
617
629
|
return {};
|
|
618
630
|
}
|
|
619
631
|
function parseInputSchema(payload) {
|
|
620
|
-
|
|
632
|
+
const schema = extractSchemas3(payload).inputSchema;
|
|
633
|
+
return schema;
|
|
621
634
|
}
|
|
622
635
|
function parseOutputSchema(payload) {
|
|
623
|
-
|
|
636
|
+
const schema = extractSchemas3(payload).outputSchema;
|
|
637
|
+
return schema;
|
|
624
638
|
}
|
|
625
639
|
|
|
626
640
|
// src/core/protocols/x402/v1/parse-payment-required.ts
|
|
@@ -848,6 +862,20 @@ function extractRequestBodySchema(operationSchema) {
|
|
|
848
862
|
}
|
|
849
863
|
return void 0;
|
|
850
864
|
}
|
|
865
|
+
function extractOutputSchema(operationSchema) {
|
|
866
|
+
const responses = operationSchema.responses;
|
|
867
|
+
if (!isRecord(responses)) return void 0;
|
|
868
|
+
const candidate = responses["200"] ?? responses["201"] ?? Object.entries(responses).find(([k]) => k.startsWith("2"))?.[1];
|
|
869
|
+
if (!isRecord(candidate)) return void 0;
|
|
870
|
+
const content = candidate.content;
|
|
871
|
+
if (!isRecord(content)) return void 0;
|
|
872
|
+
const json = content["application/json"];
|
|
873
|
+
if (isRecord(json) && isRecord(json.schema)) return json.schema;
|
|
874
|
+
for (const mediaType of Object.values(content)) {
|
|
875
|
+
if (isRecord(mediaType) && isRecord(mediaType.schema)) return mediaType.schema;
|
|
876
|
+
}
|
|
877
|
+
return void 0;
|
|
878
|
+
}
|
|
851
879
|
function extractParameters(operationSchema) {
|
|
852
880
|
const params = operationSchema.parameters;
|
|
853
881
|
if (!Array.isArray(params)) return [];
|
|
@@ -906,7 +934,7 @@ function getL3ForOpenAPI(openApi, path, method) {
|
|
|
906
934
|
estimatedPrice: parseOperationPrice(resolvedOperation),
|
|
907
935
|
protocols: parseOperationProtocols(resolvedOperation),
|
|
908
936
|
inputSchema: extractInputSchema(resolvedOperation),
|
|
909
|
-
outputSchema: resolvedOperation
|
|
937
|
+
outputSchema: extractOutputSchema(resolvedOperation)
|
|
910
938
|
};
|
|
911
939
|
}
|
|
912
940
|
function getL3ForProbe(probe, path, method) {
|
|
@@ -943,12 +971,12 @@ function getAdvisoriesForProbe(probe, path) {
|
|
|
943
971
|
});
|
|
944
972
|
}
|
|
945
973
|
async function checkEndpointSchema(options) {
|
|
946
|
-
const endpoint = new URL(options.url);
|
|
974
|
+
const endpoint = new URL(ensureProtocol(options.url));
|
|
947
975
|
const origin = normalizeOrigin(endpoint.origin);
|
|
948
976
|
const path = normalizePath(endpoint.pathname || "/");
|
|
949
977
|
if (options.sampleInputBody !== void 0) {
|
|
950
978
|
const probeResult2 = await getProbe(
|
|
951
|
-
|
|
979
|
+
endpoint.href,
|
|
952
980
|
options.headers,
|
|
953
981
|
options.signal,
|
|
954
982
|
options.sampleInputBody
|
|
@@ -973,7 +1001,7 @@ async function checkEndpointSchema(options) {
|
|
|
973
1001
|
if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
|
|
974
1002
|
return { found: false, origin, path, cause: "not_found" };
|
|
975
1003
|
}
|
|
976
|
-
const probeResult = await getProbe(
|
|
1004
|
+
const probeResult = await getProbe(endpoint.href, options.headers, options.signal);
|
|
977
1005
|
if (probeResult.isErr()) {
|
|
978
1006
|
return {
|
|
979
1007
|
found: false,
|
package/dist/index.cjs
CHANGED
|
@@ -97,6 +97,7 @@ var OpenApiDocSchema = import_zod.z.object({
|
|
|
97
97
|
description: import_zod.z.string().optional(),
|
|
98
98
|
guidance: import_zod.z.string().optional()
|
|
99
99
|
}),
|
|
100
|
+
security: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.array(import_zod.z.string()))).optional(),
|
|
100
101
|
servers: import_zod.z.array(import_zod.z.object({ url: import_zod.z.string() })).optional(),
|
|
101
102
|
tags: import_zod.z.array(import_zod.z.object({ name: import_zod.z.string() })).optional(),
|
|
102
103
|
components: import_zod.z.object({ securitySchemes: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional() }).optional(),
|
|
@@ -126,20 +127,31 @@ var WellKnownParsedSchema = import_zod.z.object({
|
|
|
126
127
|
function isRecord(value) {
|
|
127
128
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
128
129
|
}
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
function resolveSecurityFlags(requirements, securitySchemes) {
|
|
131
|
+
let hasApiKey = false;
|
|
132
|
+
let hasSiwx = false;
|
|
133
|
+
for (const requirement of requirements) {
|
|
134
|
+
for (const schemeName of Object.keys(requirement)) {
|
|
135
|
+
if (schemeName === "siwx") {
|
|
136
|
+
hasSiwx = true;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (schemeName === "apiKey") {
|
|
140
|
+
hasApiKey = true;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const def = securitySchemes[schemeName];
|
|
144
|
+
if (isRecord(def) && def["type"] === "apiKey") hasApiKey = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { hasApiKey, hasSiwx };
|
|
134
148
|
}
|
|
135
|
-
function inferAuthMode(operation) {
|
|
149
|
+
function inferAuthMode(operation, globalSecurity, securitySchemes) {
|
|
136
150
|
const hasXPaymentInfo = Boolean(operation["x-payment-info"]);
|
|
137
|
-
const
|
|
138
|
-
const hasApiKey =
|
|
139
|
-
|
|
140
|
-
if (hasPayment && hasApiKey) return "apiKey+paid";
|
|
151
|
+
const effectiveSecurity = operation.security !== void 0 && operation.security.length > 0 ? operation.security : globalSecurity ?? [];
|
|
152
|
+
const { hasApiKey, hasSiwx } = resolveSecurityFlags(effectiveSecurity, securitySchemes ?? {});
|
|
153
|
+
if (hasXPaymentInfo && hasApiKey) return "apiKey+paid";
|
|
141
154
|
if (hasXPaymentInfo) return "paid";
|
|
142
|
-
if (hasPayment) return hasSiwx ? "siwx" : "paid";
|
|
143
155
|
if (hasApiKey) return "apiKey";
|
|
144
156
|
if (hasSiwx) return "siwx";
|
|
145
157
|
return void 0;
|
|
@@ -159,10 +171,12 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
|
159
171
|
var DEFAULT_MISSING_METHOD = "POST";
|
|
160
172
|
|
|
161
173
|
// src/core/lib/url.ts
|
|
162
|
-
function
|
|
174
|
+
function ensureProtocol(target) {
|
|
163
175
|
const trimmed = target.trim();
|
|
164
|
-
|
|
165
|
-
|
|
176
|
+
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
177
|
+
}
|
|
178
|
+
function normalizeOrigin(target) {
|
|
179
|
+
const url = new URL(ensureProtocol(target));
|
|
166
180
|
url.pathname = "";
|
|
167
181
|
url.search = "";
|
|
168
182
|
url.hash = "";
|
|
@@ -201,7 +215,7 @@ function fetchSafe(url, init) {
|
|
|
201
215
|
}
|
|
202
216
|
|
|
203
217
|
// src/mmm-enabled.ts
|
|
204
|
-
var isMmmEnabled = () => "1.0.
|
|
218
|
+
var isMmmEnabled = () => "1.0.2".includes("-mmm");
|
|
205
219
|
|
|
206
220
|
// src/core/source/openapi/index.ts
|
|
207
221
|
var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
@@ -210,15 +224,13 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
|
210
224
|
for (const httpMethod of [...HTTP_METHODS]) {
|
|
211
225
|
const operation = pathItem[httpMethod.toLowerCase()];
|
|
212
226
|
if (!operation) continue;
|
|
213
|
-
const authMode = inferAuthMode(operation) ?? void 0;
|
|
227
|
+
const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
|
|
214
228
|
if (!authMode) continue;
|
|
215
229
|
const p = operation["x-payment-info"];
|
|
216
230
|
const protocols = (p?.protocols ?? []).filter(
|
|
217
231
|
(proto) => proto !== "mpp" || isMmmEnabled()
|
|
218
232
|
);
|
|
219
|
-
|
|
220
|
-
if (authMode === "paid" && protocols.length === 0) continue;
|
|
221
|
-
const pricing = authMode === "paid" && p ? {
|
|
233
|
+
const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
|
|
222
234
|
pricingMode: p.pricingMode,
|
|
223
235
|
...p.price ? { price: p.price } : {},
|
|
224
236
|
...p.minPrice ? { minPrice: p.minPrice } : {},
|
|
@@ -816,10 +828,12 @@ function extractSchemas3(payload) {
|
|
|
816
828
|
return {};
|
|
817
829
|
}
|
|
818
830
|
function parseInputSchema(payload) {
|
|
819
|
-
|
|
831
|
+
const schema = extractSchemas3(payload).inputSchema;
|
|
832
|
+
return schema;
|
|
820
833
|
}
|
|
821
834
|
function parseOutputSchema(payload) {
|
|
822
|
-
|
|
835
|
+
const schema = extractSchemas3(payload).outputSchema;
|
|
836
|
+
return schema;
|
|
823
837
|
}
|
|
824
838
|
|
|
825
839
|
// src/core/protocols/x402/v1/parse-payment-required.ts
|
|
@@ -1047,6 +1061,20 @@ function extractRequestBodySchema(operationSchema) {
|
|
|
1047
1061
|
}
|
|
1048
1062
|
return void 0;
|
|
1049
1063
|
}
|
|
1064
|
+
function extractOutputSchema(operationSchema) {
|
|
1065
|
+
const responses = operationSchema.responses;
|
|
1066
|
+
if (!isRecord(responses)) return void 0;
|
|
1067
|
+
const candidate = responses["200"] ?? responses["201"] ?? Object.entries(responses).find(([k]) => k.startsWith("2"))?.[1];
|
|
1068
|
+
if (!isRecord(candidate)) return void 0;
|
|
1069
|
+
const content = candidate.content;
|
|
1070
|
+
if (!isRecord(content)) return void 0;
|
|
1071
|
+
const json = content["application/json"];
|
|
1072
|
+
if (isRecord(json) && isRecord(json.schema)) return json.schema;
|
|
1073
|
+
for (const mediaType of Object.values(content)) {
|
|
1074
|
+
if (isRecord(mediaType) && isRecord(mediaType.schema)) return mediaType.schema;
|
|
1075
|
+
}
|
|
1076
|
+
return void 0;
|
|
1077
|
+
}
|
|
1050
1078
|
function extractParameters(operationSchema) {
|
|
1051
1079
|
const params = operationSchema.parameters;
|
|
1052
1080
|
if (!Array.isArray(params)) return [];
|
|
@@ -1105,7 +1133,7 @@ function getL3ForOpenAPI(openApi, path, method) {
|
|
|
1105
1133
|
estimatedPrice: parseOperationPrice(resolvedOperation),
|
|
1106
1134
|
protocols: parseOperationProtocols(resolvedOperation),
|
|
1107
1135
|
inputSchema: extractInputSchema(resolvedOperation),
|
|
1108
|
-
outputSchema: resolvedOperation
|
|
1136
|
+
outputSchema: extractOutputSchema(resolvedOperation)
|
|
1109
1137
|
};
|
|
1110
1138
|
}
|
|
1111
1139
|
function getL3ForProbe(probe, path, method) {
|
|
@@ -1146,12 +1174,12 @@ function getAdvisoriesForProbe(probe, path) {
|
|
|
1146
1174
|
});
|
|
1147
1175
|
}
|
|
1148
1176
|
async function checkEndpointSchema(options) {
|
|
1149
|
-
const endpoint = new URL(options.url);
|
|
1177
|
+
const endpoint = new URL(ensureProtocol(options.url));
|
|
1150
1178
|
const origin = normalizeOrigin(endpoint.origin);
|
|
1151
1179
|
const path = normalizePath(endpoint.pathname || "/");
|
|
1152
1180
|
if (options.sampleInputBody !== void 0) {
|
|
1153
1181
|
const probeResult2 = await getProbe(
|
|
1154
|
-
|
|
1182
|
+
endpoint.href,
|
|
1155
1183
|
options.headers,
|
|
1156
1184
|
options.signal,
|
|
1157
1185
|
options.sampleInputBody
|
|
@@ -1176,7 +1204,7 @@ async function checkEndpointSchema(options) {
|
|
|
1176
1204
|
if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
|
|
1177
1205
|
return { found: false, origin, path, cause: "not_found" };
|
|
1178
1206
|
}
|
|
1179
|
-
const probeResult = await getProbe(
|
|
1207
|
+
const probeResult = await getProbe(endpoint.href, options.headers, options.signal);
|
|
1180
1208
|
if (probeResult.isErr()) {
|
|
1181
1209
|
return {
|
|
1182
1210
|
found: false,
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ var OpenApiDocSchema = z.object({
|
|
|
50
50
|
description: z.string().optional(),
|
|
51
51
|
guidance: z.string().optional()
|
|
52
52
|
}),
|
|
53
|
+
security: z.array(z.record(z.string(), z.array(z.string()))).optional(),
|
|
53
54
|
servers: z.array(z.object({ url: z.string() })).optional(),
|
|
54
55
|
tags: z.array(z.object({ name: z.string() })).optional(),
|
|
55
56
|
components: z.object({ securitySchemes: z.record(z.string(), z.unknown()).optional() }).optional(),
|
|
@@ -79,20 +80,31 @@ var WellKnownParsedSchema = z.object({
|
|
|
79
80
|
function isRecord(value) {
|
|
80
81
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
function resolveSecurityFlags(requirements, securitySchemes) {
|
|
84
|
+
let hasApiKey = false;
|
|
85
|
+
let hasSiwx = false;
|
|
86
|
+
for (const requirement of requirements) {
|
|
87
|
+
for (const schemeName of Object.keys(requirement)) {
|
|
88
|
+
if (schemeName === "siwx") {
|
|
89
|
+
hasSiwx = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (schemeName === "apiKey") {
|
|
93
|
+
hasApiKey = true;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const def = securitySchemes[schemeName];
|
|
97
|
+
if (isRecord(def) && def["type"] === "apiKey") hasApiKey = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { hasApiKey, hasSiwx };
|
|
87
101
|
}
|
|
88
|
-
function inferAuthMode(operation) {
|
|
102
|
+
function inferAuthMode(operation, globalSecurity, securitySchemes) {
|
|
89
103
|
const hasXPaymentInfo = Boolean(operation["x-payment-info"]);
|
|
90
|
-
const
|
|
91
|
-
const hasApiKey =
|
|
92
|
-
|
|
93
|
-
if (hasPayment && hasApiKey) return "apiKey+paid";
|
|
104
|
+
const effectiveSecurity = operation.security !== void 0 && operation.security.length > 0 ? operation.security : globalSecurity ?? [];
|
|
105
|
+
const { hasApiKey, hasSiwx } = resolveSecurityFlags(effectiveSecurity, securitySchemes ?? {});
|
|
106
|
+
if (hasXPaymentInfo && hasApiKey) return "apiKey+paid";
|
|
94
107
|
if (hasXPaymentInfo) return "paid";
|
|
95
|
-
if (hasPayment) return hasSiwx ? "siwx" : "paid";
|
|
96
108
|
if (hasApiKey) return "apiKey";
|
|
97
109
|
if (hasSiwx) return "siwx";
|
|
98
110
|
return void 0;
|
|
@@ -112,10 +124,12 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
|
112
124
|
var DEFAULT_MISSING_METHOD = "POST";
|
|
113
125
|
|
|
114
126
|
// src/core/lib/url.ts
|
|
115
|
-
function
|
|
127
|
+
function ensureProtocol(target) {
|
|
116
128
|
const trimmed = target.trim();
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
130
|
+
}
|
|
131
|
+
function normalizeOrigin(target) {
|
|
132
|
+
const url = new URL(ensureProtocol(target));
|
|
119
133
|
url.pathname = "";
|
|
120
134
|
url.search = "";
|
|
121
135
|
url.hash = "";
|
|
@@ -154,7 +168,7 @@ function fetchSafe(url, init) {
|
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
// src/mmm-enabled.ts
|
|
157
|
-
var isMmmEnabled = () => "1.0.
|
|
171
|
+
var isMmmEnabled = () => "1.0.2".includes("-mmm");
|
|
158
172
|
|
|
159
173
|
// src/core/source/openapi/index.ts
|
|
160
174
|
var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
@@ -163,15 +177,13 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
|
163
177
|
for (const httpMethod of [...HTTP_METHODS]) {
|
|
164
178
|
const operation = pathItem[httpMethod.toLowerCase()];
|
|
165
179
|
if (!operation) continue;
|
|
166
|
-
const authMode = inferAuthMode(operation) ?? void 0;
|
|
180
|
+
const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
|
|
167
181
|
if (!authMode) continue;
|
|
168
182
|
const p = operation["x-payment-info"];
|
|
169
183
|
const protocols = (p?.protocols ?? []).filter(
|
|
170
184
|
(proto) => proto !== "mpp" || isMmmEnabled()
|
|
171
185
|
);
|
|
172
|
-
|
|
173
|
-
if (authMode === "paid" && protocols.length === 0) continue;
|
|
174
|
-
const pricing = authMode === "paid" && p ? {
|
|
186
|
+
const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
|
|
175
187
|
pricingMode: p.pricingMode,
|
|
176
188
|
...p.price ? { price: p.price } : {},
|
|
177
189
|
...p.minPrice ? { minPrice: p.minPrice } : {},
|
|
@@ -769,10 +781,12 @@ function extractSchemas3(payload) {
|
|
|
769
781
|
return {};
|
|
770
782
|
}
|
|
771
783
|
function parseInputSchema(payload) {
|
|
772
|
-
|
|
784
|
+
const schema = extractSchemas3(payload).inputSchema;
|
|
785
|
+
return schema;
|
|
773
786
|
}
|
|
774
787
|
function parseOutputSchema(payload) {
|
|
775
|
-
|
|
788
|
+
const schema = extractSchemas3(payload).outputSchema;
|
|
789
|
+
return schema;
|
|
776
790
|
}
|
|
777
791
|
|
|
778
792
|
// src/core/protocols/x402/v1/parse-payment-required.ts
|
|
@@ -1000,6 +1014,20 @@ function extractRequestBodySchema(operationSchema) {
|
|
|
1000
1014
|
}
|
|
1001
1015
|
return void 0;
|
|
1002
1016
|
}
|
|
1017
|
+
function extractOutputSchema(operationSchema) {
|
|
1018
|
+
const responses = operationSchema.responses;
|
|
1019
|
+
if (!isRecord(responses)) return void 0;
|
|
1020
|
+
const candidate = responses["200"] ?? responses["201"] ?? Object.entries(responses).find(([k]) => k.startsWith("2"))?.[1];
|
|
1021
|
+
if (!isRecord(candidate)) return void 0;
|
|
1022
|
+
const content = candidate.content;
|
|
1023
|
+
if (!isRecord(content)) return void 0;
|
|
1024
|
+
const json = content["application/json"];
|
|
1025
|
+
if (isRecord(json) && isRecord(json.schema)) return json.schema;
|
|
1026
|
+
for (const mediaType of Object.values(content)) {
|
|
1027
|
+
if (isRecord(mediaType) && isRecord(mediaType.schema)) return mediaType.schema;
|
|
1028
|
+
}
|
|
1029
|
+
return void 0;
|
|
1030
|
+
}
|
|
1003
1031
|
function extractParameters(operationSchema) {
|
|
1004
1032
|
const params = operationSchema.parameters;
|
|
1005
1033
|
if (!Array.isArray(params)) return [];
|
|
@@ -1058,7 +1086,7 @@ function getL3ForOpenAPI(openApi, path, method) {
|
|
|
1058
1086
|
estimatedPrice: parseOperationPrice(resolvedOperation),
|
|
1059
1087
|
protocols: parseOperationProtocols(resolvedOperation),
|
|
1060
1088
|
inputSchema: extractInputSchema(resolvedOperation),
|
|
1061
|
-
outputSchema: resolvedOperation
|
|
1089
|
+
outputSchema: extractOutputSchema(resolvedOperation)
|
|
1062
1090
|
};
|
|
1063
1091
|
}
|
|
1064
1092
|
function getL3ForProbe(probe, path, method) {
|
|
@@ -1099,12 +1127,12 @@ function getAdvisoriesForProbe(probe, path) {
|
|
|
1099
1127
|
});
|
|
1100
1128
|
}
|
|
1101
1129
|
async function checkEndpointSchema(options) {
|
|
1102
|
-
const endpoint = new URL(options.url);
|
|
1130
|
+
const endpoint = new URL(ensureProtocol(options.url));
|
|
1103
1131
|
const origin = normalizeOrigin(endpoint.origin);
|
|
1104
1132
|
const path = normalizePath(endpoint.pathname || "/");
|
|
1105
1133
|
if (options.sampleInputBody !== void 0) {
|
|
1106
1134
|
const probeResult2 = await getProbe(
|
|
1107
|
-
|
|
1135
|
+
endpoint.href,
|
|
1108
1136
|
options.headers,
|
|
1109
1137
|
options.signal,
|
|
1110
1138
|
options.sampleInputBody
|
|
@@ -1129,7 +1157,7 @@ async function checkEndpointSchema(options) {
|
|
|
1129
1157
|
if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
|
|
1130
1158
|
return { found: false, origin, path, cause: "not_found" };
|
|
1131
1159
|
}
|
|
1132
|
-
const probeResult = await getProbe(
|
|
1160
|
+
const probeResult = await getProbe(endpoint.href, options.headers, options.signal);
|
|
1133
1161
|
if (probeResult.isErr()) {
|
|
1134
1162
|
return {
|
|
1135
1163
|
found: false,
|
package/dist/schemas.cjs
CHANGED
|
@@ -76,6 +76,7 @@ var OpenApiDocSchema = import_zod.z.object({
|
|
|
76
76
|
description: import_zod.z.string().optional(),
|
|
77
77
|
guidance: import_zod.z.string().optional()
|
|
78
78
|
}),
|
|
79
|
+
security: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.array(import_zod.z.string()))).optional(),
|
|
79
80
|
servers: import_zod.z.array(import_zod.z.object({ url: import_zod.z.string() })).optional(),
|
|
80
81
|
tags: import_zod.z.array(import_zod.z.object({ name: import_zod.z.string() })).optional(),
|
|
81
82
|
components: import_zod.z.object({ securitySchemes: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional() }).optional(),
|
package/dist/schemas.d.cts
CHANGED
|
@@ -300,6 +300,7 @@ declare const OpenApiDocSchema: z.ZodObject<{
|
|
|
300
300
|
description: z.ZodOptional<z.ZodString>;
|
|
301
301
|
guidance: z.ZodOptional<z.ZodString>;
|
|
302
302
|
}, z.core.$strip>;
|
|
303
|
+
security: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>>;
|
|
303
304
|
servers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
304
305
|
url: z.ZodString;
|
|
305
306
|
}, z.core.$strip>>>;
|
package/dist/schemas.d.ts
CHANGED
|
@@ -300,6 +300,7 @@ declare const OpenApiDocSchema: z.ZodObject<{
|
|
|
300
300
|
description: z.ZodOptional<z.ZodString>;
|
|
301
301
|
guidance: z.ZodOptional<z.ZodString>;
|
|
302
302
|
}, z.core.$strip>;
|
|
303
|
+
security: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>>;
|
|
303
304
|
servers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
304
305
|
url: z.ZodString;
|
|
305
306
|
}, z.core.$strip>>>;
|
package/dist/schemas.js
CHANGED
|
@@ -47,6 +47,7 @@ var OpenApiDocSchema = z.object({
|
|
|
47
47
|
description: z.string().optional(),
|
|
48
48
|
guidance: z.string().optional()
|
|
49
49
|
}),
|
|
50
|
+
security: z.array(z.record(z.string(), z.array(z.string()))).optional(),
|
|
50
51
|
servers: z.array(z.object({ url: z.string() })).optional(),
|
|
51
52
|
tags: z.array(z.object({ name: z.string() })).optional(),
|
|
52
53
|
components: z.object({ securitySchemes: z.record(z.string(), z.unknown()).optional() }).optional(),
|