Module: Portage::Ucp::Client
- Defined in:
- lib/portage/ucp/client.rb,
lib/portage/ucp/client/errors.rb,
lib/portage/ucp/client/session.rb,
lib/portage/ucp/client/version.rb,
lib/portage/ucp/client/tool_result.rb,
lib/portage/ucp/client/transports/http.rb,
lib/portage/ucp/client/transports/stdio.rb,
lib/portage/ucp/client/transports/loopback.rb,
lib/portage/ucp/client/transports/ucp_wire_shape.rb,
lib/portage/ucp/client/transports/local_arguments.rb,
lib/portage/ucp/client/transports/http/complete_checkout_wire_shape.rb
Overview
Client-side counterpart to the rest of portage-ucp: everything else in this repo lets a Ruby program expose a commerce backend to agents (server side). This gem lets a Ruby program act as the shopper's agent — discover somebody else's manifest, or drive an owner's own Adapter directly, and place an order.
Defined Under Namespace
Modules: ToolResult, Transports Classes: DiscoveryError, Error, ManifestShapeError, MissingAgentProfileError, PaymentPermissionError, ServerError, Session, UnsupportedWireShapeError
Constant Summary collapse
- MANIFEST_PATH =
"/.well-known/ucp".freeze
- USER_AGENT =
Sent on every request this gem makes to a store (the manifest GET and each Streamable HTTP call) unless the caller's own headers name one, so a merchant reading its logs can tell a Portage agent from Ruby's or Faraday's default and find out what it is.
"portage-ucp-client/#{VERSION} (+https://github.com/tomtom87/Portage)".freeze
- VERSION =
"0.6.3".freeze
Class Method Summary collapse
-
.connect(command: nil, args: [], env: nil, url: nil, headers: {}, capabilities: nil, proxy: nil) ⇒ Object
Connects over stdio (a subprocess) or Streamable HTTP (a URL) — pass exactly one of
command:orurl:. -
.discover(url, headers: {}, proxy: nil) ⇒ Session
GETs
<url>/.well-known/ucp, parses the manifest, and connects to themcp-transport endpoint it advertises inservices(see Portage::Ucp::Manifest#services — the core-gem fix this client depends on to know where to connect). -
.for_adapter(adapter, **server_opts) ⇒ Object
Wraps an already-built Adapter directly — no subprocess/socket.
-
.with_user_agent(headers) ⇒ Object
USER_AGENT unless
headersalready names one, in any case.
Class Method Details
.connect(command: nil, args: [], env: nil, url: nil, headers: {}, capabilities: nil, proxy: nil) ⇒ Object
Connects over stdio (a subprocess) or Streamable HTTP (a URL) — pass
exactly one of command: or url:.
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/portage/ucp/client.rb', line 45 def self.connect(command: nil, args: [], env: nil, url: nil, headers: {}, capabilities: nil, proxy: nil) transport = if command Transports::Stdio.new(command: command, args: args, env: env) elsif url Transports::Http.new(url: url, headers: headers, proxy: proxy) else raise ArgumentError, "connect requires either command: or url:" end Session.new(transport: transport, capabilities: capabilities) end |
.discover(url, headers: {}, proxy: nil) ⇒ Session
GETs <url>/.well-known/ucp, parses the manifest, and connects to the
mcp-transport endpoint it advertises in services (see
Portage::Ucp::Manifest#services — the core-gem fix this client
depends on to know where to connect).
66 67 68 69 |
# File 'lib/portage/ucp/client.rb', line 66 def self.discover(url, headers: {}, proxy: nil) manifest = fetch_manifest(url, headers) connect(url: mcp_endpoint(manifest), headers: headers, capabilities: capability_names(manifest), proxy: proxy) end |
.for_adapter(adapter, **server_opts) ⇒ Object
Wraps an already-built Adapter directly — no subprocess/socket. Still runs the real merchant-side contract (authenticator, rate limiter, Dispatcher, WireEnvelope, via Portage::Ucp::Mcp::Server), just in-process. This is the transport for the "your own store" case: you already have credentials for this Adapter, so there's no manifest to discover and no wire hop to make.
37 38 39 |
# File 'lib/portage/ucp/client.rb', line 37 def self.for_adapter(adapter, **server_opts) Session.new(transport: Transports::Loopback.new(adapter: adapter, **server_opts)) end |
.with_user_agent(headers) ⇒ Object
USER_AGENT unless headers already names one, in any case.
87 88 89 90 91 |
# File 'lib/portage/ucp/client.rb', line 87 def self.with_user_agent(headers) return headers if headers.keys.any? { |name| name.to_s.casecmp?("user-agent") } { "User-Agent" => USER_AGENT }.merge(headers) end |