Merge remote-tracking branch 'origin/main' into codex/octen-catalog

# Conflicts:
#	dsh/skills/treg/SKILL.md
#	plugin/skills/treg/SKILL.md
#	plugins/minimax/skills/treg/SKILL.md
#	plugins/treg/skills/treg/SKILL.md
#	skills/treg/SKILL.md
#	src/treg/oauth_providers.py
This commit is contained in:
shehjad-dev
2026-09-30 03:37:59 +06:00
24 changed files with 1408 additions and 13 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,700+ endpoints across 105 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
---
## First, check which treg you have
@@ -100,7 +100,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,700+ catalogued endpoints across 105 providers, grouped by what they DO: keyword & rank tracking,
3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking,
backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social
accounts, people & company enrichment, ads management & creative, measurement, video & image
generation.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,700+ endpoints across 105 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
---
## First, check which treg you have
@@ -103,7 +103,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,700+ catalogued endpoints across 105 providers, grouped by what they DO: keyword & rank tracking,
3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking,
backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social
accounts, people & company enrichment, ads management & creative, measurement, video & image
generation.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,700+ endpoints across 105 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
version: 0.22.0
---
@@ -84,7 +84,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,700+ catalogued endpoints across 105 providers, grouped by what they DO: keyword & rank tracking,
3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking,
backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social
accounts, people & company enrichment, ads management & creative, measurement, video & image
generation.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,700+ endpoints across 105 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
version: 0.22.0
---
@@ -98,7 +98,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,700+ catalogued endpoints across 105 providers, grouped by what they DO: keyword & rank tracking,
3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking,
backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social
accounts, people & company enrichment, ads management & creative, measurement, video & image
generation.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,700+ endpoints across 105 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later.
version: 0.22.0
---
@@ -82,7 +82,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,700+ catalogued endpoints across 105 providers, grouped by what they DO: keyword & rank tracking,
3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking,
backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social
accounts, people & company enrichment, ads management & creative, measurement, video & image
generation.
+8
View File
@@ -483,6 +483,14 @@ def _observed_cost_micro(mk: MarketplaceCall, body: bytes, headers=None) -> int
doc = json.loads(body)
except (ValueError, UnicodeDecodeError):
return 0 if provider == "contactout" else None
if (provider == "valyu" and mk.endpoint_id.endswith(".search")
and isinstance(doc, dict)
and ("results" not in doc or doc["results"] == [])
and doc.get("total_deduction_dollars") is None):
# Search can answer 206 with no results and a null deduction (observed live).
# An omitted results key is also no evidence of a billable retrieval.
# A usage basis would otherwise settle at the reserved result count.
return 0
if provider == "you" and mk.endpoint_id == "you.web.contents" and mk.cost_type == "per_result":
# Contents returns one object per fetched page as a bare array. The request's URL count
# bounds the hold; count only pages the provider actually returned.
+7
View File
@@ -2656,6 +2656,13 @@ adapters:
in_expr: {body.count: limit}
out: {results: results.web, count: "len(results.web)"}
miss: "coalesce(results.web, []) == []"
valyu.web.search:
accepts: [[q]]
in: {q: body.query}
in_expr: {body.max_num_results: limit}
const: {body.search_type: web, body.max_price: 1.5, body.response_length: short}
out: {results: results, count: "len(results)"}
miss: "coalesce(results, []) == []"
tinyfish.web.search:
accepts: [[q]]
in: {q: queryParams.query}
+4
View File
@@ -128,6 +128,10 @@ platforms:
# --- Market data: prices & financial markets --------------------------------------------------
crypto: {label: "Crypto Market Data", category: "Market data", featured: 1, summary: "Live and historical prices, market caps, trends and exchanges across 17,000+ coins."}
stocks: {label: "Stock Market Data", category: "Market data", featured: 2, summary: "End-of-day and intraday stock prices, tickers, splits and dividends across global exchanges."}
markets: {label: "Other market data", category: "Market data", summary: "Foreign exchange, commodities, movers and prediction markets."}
research: {label: "Academic literature", category: "Research & public data", summary: "Search scholarly papers and preprints across academic sources."}
health: {label: "Health & biomedical", category: "Research & public data", summary: "Clinical studies, medicine labels and public health statistics."}
economics: {label: "Economic indicators", category: "Research & public data", summary: "Official labor and development statistics."}
capabilities:
# --- AI generation ---------------------------------------------------------------------------
@@ -0,0 +1,18 @@
{
"success": true,
"query": "What is example.com?",
"results": [
{
"id": "https://en.wikipedia.org/wiki/Example.com",
"title": "example.com - Wikipedia",
"url": "https://en.wikipedia.org/wiki/Example.com",
"source": "web",
"price": 0.0015
}
],
"results_by_source": {
"web": 1,
"proprietary": 0
},
"total_deduction_dollars": 0.0015
}
+1
View File
@@ -28,6 +28,7 @@
# NOTE: this block must stay ABOVE `rates_to_usd:` -- catalog_fx_update.py rewrites the file
# from the text before that key, so anything below it is discarded on the next FX refresh.
credit_rates_usd:
valyu: {usd: 1.0, basis: "Pay-as-you-go replacement cost: $1 per credit. Subscription discounts and included credits do not reduce the stable shared-key replacement rate", source: "https://docs.valyu.ai/pricing", checked: "2026-09-30"}
firecrawl: {usd: 0.005, basis: "Public base-plan extra-credit rate: $5 / 1,000 credits = $0.005 per credit", source: "https://www.firecrawl.dev/pricing", checked: "2026-09-28"}
scrapegraphai: {usd: 0.004, basis: "Acquired account rate: $4 / 1,000 credits = $0.004 per credit. Subscription versus one-time replenishment is not yet fixed, so this records the actual shared-account acquisition rate", source: "ScrapeGraphAI account purchase rate", checked: "2026-09-24"}
serper: {usd: 0.001, basis: "Starter top-up replacement cost: $50 / 50,000 credits = $0.001 per credit. The current promotional balance does not reduce the sustainable platform rate", source: "https://serper.dev/#pricing", checked: "2026-09-24"}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -267,6 +267,7 @@ class Settings(BaseSettings):
platform_key_tavily: str = "" # Bearer; Search reports per-call usage, other tools settle returned successes
platform_key_linkup: str = "" # Bearer; prepaid USD balance, request-priced Search/Fetch/Research
platform_key_you: str = "" # X-API-Key; prepaid USD balance across You.com web APIs
platform_key_valyu: str = "" # X-API-Key; subscription credits shared across Valyu APIs
platform_key_serper: str = "" # X-API-KEY; prepaid Google search credits, exact charge in response.credits
platform_key_litescrape: str = "" # Bearer; prepaid calls, free key status endpoint
platform_key_keenable: str = "" # X-API-Key; $4/1,000-request package, 10 requests/s per organization
+2
View File
@@ -904,6 +904,8 @@ BALANCE_ROUTES = {
# obtain. Kept explicit so the report names them instead of silently skipping, and so a future probe
# has a list of what to re-check.
NO_BALANCE_API = {
"valyu": "no documented API endpoint for remaining credits or account usage; "
"the subscription balance is visible in Valyu's dashboard",
"perplexity": "no API credit-balance endpoint in the published API reference; "
"the prepaid USD balance is visible in the Perplexity API console",
"adyntel": "no public balance or usage endpoint in the official API reference "
+4
View File
@@ -47,6 +47,7 @@ _KNOWN: dict[str, tuple[str, str, str]] = {
"tavily": ("credits", "manual", "api"),
"linkup": ("cash", "manual", "api"),
"you": ("cash", "auto_recharge", "api"),
"valyu": ("credits", "subscription", "manual"),
# The API supplies the exact credit balance; vendor auto recharge was manually enabled and
# verified in the Serper dashboard.
"serper": ("credits", "auto_recharge", "api"),
@@ -147,6 +148,9 @@ _RATE_LIMITS: dict[str, dict] = {
# returned no rate-limit headers. Smooth the shared key conservatively until the vendor supplies
# a contract value or production traffic establishes a safer bound. BYOK bypasses this policy.
"olostep": {"limit": 5, "window_s": 1, "source": "policy"},
# Valyu publishes no numeric self-serve limit and successful calls returned no rate headers.
# This is a shared-key pacing policy, not a claim about the provider's allowance.
"valyu": {"limit": 5, "window_s": 1, "source": "policy"},
# Standard's strictest shared submission limit is 100/min for Crawl and Batch Scrape.
# Scrape, Search and Map allow 500/min; provider-wide smoothing uses the lower ceiling.
"firecrawl": {"limit": 100, "window_s": 60, "source": "docs"},
+23 -2
View File
@@ -2610,6 +2610,27 @@ YOU = OAuthProvider(
probe_path="/v1/billing/account_balance",
)
VALYU = OAuthProvider(
service="valyu",
display_name="Valyu",
auth_kind="key",
token_label="API key",
token_placeholder="your Valyu API key",
token_header="X-API-Key",
token_format="{secret}",
setup_url="https://platform.valyu.ai",
setup_action_label="Get your Valyu API key",
setup_steps=("Sign in to Valyu Platform and open API Keys.", "Create or copy a key."),
setup_note="Search, Contents, Answer and DeepResearch use Valyu credits. Datasource discovery checks the key without a paid query.",
auth_uri="", token_uri="", scopes={},
client_id_setting="", client_secret_setting="",
category="Research & public data",
summary="Search the web and specialist sources, extract pages, and run cited research.",
base_url="https://api.valyu.ai",
docs_url="https://docs.valyu.ai/home",
probe_path="/v1/datasources",
)
SERPER = OAuthProvider(
service="serper",
display_name="Serper",
@@ -3788,7 +3809,7 @@ REGISTRY: dict[str, OAuthProvider] = {
TIKHUB, BRIGHTDATA, SEMRUSH, JUSTONEAPI,
SCRAPECREATORS,
# SEO API-key providers
DATAFORSEO, SERANKING, MOZ, MAJESTIC, SERPSTAT, EXA, TAVILY, OCTEN, LINKUP, YOU, KEENABLE, OLOSTEP, FIRECRAWL, SPIDERCLOUD, PERPLEXITY,
DATAFORSEO, SERANKING, MOZ, MAJESTIC, SERPSTAT, EXA, TAVILY, OCTEN, LINKUP, YOU, VALYU, KEENABLE, OLOSTEP, FIRECRAWL, SPIDERCLOUD, PERPLEXITY,
SCRAPEGRAPHAI, SERPER, LITESCRAPE, CLORO,
# more Enrichment API-key providers
LUSHA, CORESIGNAL, DIFFBOT, THECOMPANIESAPI, LEADMAGIC, FIBER_AI, CRUSTDATA, AVIATO,
@@ -3808,7 +3829,7 @@ DEFAULT_CAPABILITY = "read"
# Shelf order in the marketplace. Anything carrying a category not named here sorts last, so a
# provider added without one is visible rather than lost between the shelves.
CATEGORY_ORDER = ("AI generation", "SEO", "Advertising", "Social media", "Enrichment",
"Market data", "Community", "Other")
"Market data", "Research & public data", "Community", "Other")
def get(service: str) -> OAuthProvider | None:
+1
View File
@@ -177,6 +177,7 @@ CATALOG: list[dict] = [
{"provider": "Octen", "tokens": ["OCTEN"], "base_url": "https://api.octen.ai", "auth": {"shape": "api_key_header", "header": "x-api-key"}},
{"provider": "Linkup", "tokens": ["LINKUP"], "base_url": "https://api.linkup.so/v1", "auth": {"shape": "bearer"}, "probe": "credits/balance"},
{"provider": "You.com", "tokens": ["YDC_API_KEY", "YOU_API_KEY"], "base_url": "https://api.you.com", "auth": {"shape": "api_key_header", "header": "X-API-Key"}, "probe": "v1/billing/account_balance"},
{"provider": "Valyu", "tokens": ["VALYU_API_KEY", "VALYU"], "base_url": "https://api.valyu.ai", "auth": {"shape": "api_key_header", "header": "X-API-Key"}, "probe": "v1/datasources"},
{"provider": "Keenable", "tokens": ["KEENABLE"], "base_url": "https://api.keenable.ai", "auth": {"shape": "api_key_header", "header": "X-API-Key"}},
{"provider": "Olostep", "tokens": ["OLOSTEP"], "base_url": "https://api.olostep.com", "auth": {"shape": "bearer"}, "probe": "user/credits/info"},
{"provider": "ScrapeGraphAI", "tokens": ["SCRAPEGRAPHAI", "SGAI"],
@@ -0,0 +1,6 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Economic indicators">
<path d="M3 19.5h18" fill="none" stroke="#436787" stroke-width="1.5" stroke-linecap="round"/>
<rect x="4" y="12" width="3.5" height="7" rx="1" fill="#6A99BD"/>
<rect x="10.25" y="8" width="3.5" height="11" rx="1" fill="#4C7FA9"/>
<rect x="16.5" y="4" width="3.5" height="15" rx="1" fill="#315F88"/>
</svg>

After

Width:  |  Height:  |  Size: 419 B

+4
View File
@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Health and biomedical data">
<rect x="3" y="3" width="18" height="18" rx="5" fill="#E8F6F3"/>
<path d="M10 6h4v4h4v4h-4v4h-4v-4H6v-4h4V6Z" fill="#238578"/>
</svg>

After

Width:  |  Height:  |  Size: 250 B

+5
View File
@@ -0,0 +1,5 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Other market data">
<rect x="2.5" y="3" width="19" height="18" rx="3" fill="#EDF5F0"/>
<path d="M5 16.5l4.4-4.2 3.2 2.1 5.9-6" fill="none" stroke="#2E8B57" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="18.5" cy="8.4" r="1.5" fill="#2E8B57"/>
</svg>

After

Width:  |  Height:  |  Size: 370 B

@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Academic literature">
<path d="M12 5.5c-2.7-1.8-5.7-2.1-9-1.3v13.7c3.4-.8 6.4-.4 9 1.3 2.6-1.7 5.6-2.1 9-1.3V4.2c-3.3-.8-6.3-.5-9 1.3Z" fill="#EDE9F8" stroke="#6B4DA0" stroke-width="1.5" stroke-linejoin="round"/>
<path d="M12 5.5v13.7M5.5 8c1.4-.1 2.7.1 4 .6M14.5 8.6c1.3-.5 2.6-.7 4-.6" fill="none" stroke="#6B4DA0" stroke-width="1.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 455 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="150" height="150" viewBox="0 0 150 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="150" height="150" rx="24" fill="black"/>
<path d="M75.8975 121.647C75.531 122.392 74.469 122.392 74.1025 121.647L33.8896 39.9414C33.5627 39.2769 34.0465 38.5 34.7871 38.5L115.213 38.5C115.953 38.5 116.437 39.2769 116.11 39.9414L75.8975 121.647Z" fill="black" stroke="white" stroke-width="4"/>
</svg>

After

Width:  |  Height:  |  Size: 413 B

+1
View File
@@ -311,6 +311,7 @@ _UNRECORDED_SIGNATURE = {
"perplexity", # auto top-up is enabled; no empty-credit response was forced
"trestleiq", # funded wallet remains; documented 403/429 shapes do not identify empty balance
"you", # funded wallet remains; no provider-specific empty-balance response was forced
"valyu", # subscription credits remain; no provider-specific empty-balance response was forced
"serpapi", "serpstat", "spyfu", "tiingo", "tikhub", "tomba", "twelvedata",
}
+16
View File
@@ -992,6 +992,22 @@ def test_observed_cost_only_trusts_a_real_number():
assert call_settle._observed_cost_micro(_mk("akta"), b'{"credits_charged": 2}') is None, "wrong field name means we never learned it"
def test_valyu_empty_search_releases_the_result_reserve():
mk = _mk("valyu", endpoint_id="valyu.markets.predictions.search", cost_type="per_call")
assert call_settle._observed_cost_micro(
mk, b'{"results": [], "total_deduction_dollars": null}'
) == 0
assert call_settle._observed_cost_micro(
mk, b'{"total_deduction_dollars": null}'
) == 0
assert call_settle._observed_cost_micro(
mk, b'{"results": null, "total_deduction_dollars": null}'
) is None # malformed results are not proof of an empty search
assert call_settle._observed_cost_micro(
mk, b'{"results": [{"id": "event"}], "total_deduction_dollars": 0.005}'
) is None # the declarative usage basis reads the reported amount
# Providers whose body carries no billing field report the call's charge in a response header.
_CRUSTDATA = ("crustdata", {"endpoint_id": "crustdata.companies.search"}, b'{"rows": []}')
# AI Ark reports a debit as a NEGATIVE X-Credit value; the sign rule is provider-specific.
+24 -1
View File
@@ -4,6 +4,7 @@
from __future__ import annotations
import asyncio
from copy import deepcopy
import json
from types import SimpleNamespace
@@ -15,9 +16,10 @@ from sqlmodel import select
from treg import audit
from treg.domain import money as ledger
from treg.application.call import overflow as call_overflow
from treg.application.call import resolve as call_resolve
from treg.application.call import route as call_route
from treg.application.call import service as call_service
from treg.application.call.types import UpstreamResponse
from treg.application.call.types import ResolutionFailed, UpstreamResponse
from treg.config import get_settings
from treg.infra.db import session_maker
from treg.domain.catalog import store as catalog_store
@@ -122,6 +124,27 @@ def test_every_shipped_adapter_round_trips_its_fixture():
assert ad.is_miss({"email": None}) and not ad.is_miss({"email": "x"})
def test_every_adapter_const_passes_its_child_body_allowlist():
cat = catalog_store.load()
failures = {}
for endpoint_id, adapter in cat.adapters.items():
ep = cat.by_id[endpoint_id]
body_consts = {path: value for path, value in adapter.const.items()
if path.startswith("body.")}
if not body_consts or not (ep.get("body_allowlist") or ep.get("strict_body")):
continue
body = deepcopy((ep.get("test_request") or {}).get("body"))
assert isinstance(body, dict), endpoint_id
request = {"body": body}
for path, value in body_consts.items():
P.set_path(request, path, value)
try:
call_resolve._enforce_catalog_body(ep, json.dumps(body).encode())
except ResolutionFailed as exc:
failures[endpoint_id] = exc.detail
assert failures == {}, failures
def test_firecrawl_web_adapters_are_verified_routed_children():
cat = catalog_store.load()
for parent, child in (