Connectors and capabilities
ConnectorFabric is the catalog and construction boundary. Known built-ins remain discoverable even when their extras are not installed.
from vendor_fabric import ConnectorFabric
fabric = ConnectorFabric()
print(fabric.list_connector_categories())
print(fabric.list_connectors_by_capability("repositories"))
info = fabric.get_connector_info("github")
if info.available:
github = fabric.get_connector("github", github_owner="jbcom", github_token="...")
else:
print(info.install)Direct construction is also available when it is clearer:
from vendor_fabric import GitHubConnector, SlackConnector
github = GitHubConnector(github_owner="jbcom", github_token="...")
slack = SlackConnector(bot_token="xoxb-...")Connector methods promote decoded payloads to the ExtendedData family (ExtendedDict, ExtendedList, or ExtendedString) at the public boundary.
Generic dispatch
Providers declare capability metadata through @capability. VendorData uses that metadata to select an opened provider or to route an explicit provider id.
from vendor_fabric.vendor_data import VendorData
data = VendorData({"resource": "config"})
data.open("aws")
result = data.call("get_object", "aws", bucket="my-bucket", key="config.json")Do not add a hard-coded facade method when a provider capability and VendorData.call express the same contract.
Webhook safety
The optional Cursor connector accepts webhook callbacks only over HTTPS. Before a webhook is used, it rejects loopback, private, link-local, unspecified, and cloud-metadata addresses so a caller cannot use the connector to reach an internal service.
Steam sessions and redemption limits
The Steam connector implements Steam's IAuthenticationService login flow
directly over HTTPS, so no third-party Steam SDK is required. Only the RSA step
needs an extra: pip install vendor-fabric[steam].
from vendor_fabric import SteamConnector
with SteamConnector(account_name="user", password="...") as steam:
steam.authenticate(steam_guard_code="ABCDE")
owned = steam.list_owned_apps()
outcome = steam.redeem_key("AAAAA-BBBBB-CCCCC")Two behaviours are easy to get wrong and are handled explicitly:
- Session ids are per-domain. Steam issues a different
sessionidcookie forstore.,help., andsteamcommunity.com. Flattening the jar yields a token the store rejects, soSteamSession.session_id(domain)always takes the domain being posted to. - Failures are scarcer than successes. Steam permits roughly 50 activations
per hour but only 10 failed ones, and duplicates count as failures. Check
ownership with
list_owned_apps()before redeeming.redeem_keys()stops as soon as Steam reportsRedemptionResult.RATE_LIMITED, because further attempts during a cooldown only extend it.
A missing purchase_result_details is reported as RedemptionResult.UNKNOWN
rather than being assumed to be a rate limit, so an unexpected failure surfaces
instead of turning into an unbounded retry.
Errors arrive in the x-eresult response header rather than the HTTP status or
body — a rejected password is an HTTP 200 with an empty JSON body — so
responses are validated against that header.
