portage-ucp-client 0.6.1 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/lib/portage/ucp/client/transports/http.rb +2 -100
- data/lib/portage/ucp/client/transports/local_arguments.rb +30 -0
- data/lib/portage/ucp/client/transports/loopback.rb +5 -9
- data/lib/portage/ucp/client/transports/stdio.rb +5 -9
- data/lib/portage/ucp/client/transports/ucp_wire_shape.rb +121 -0
- data/lib/portage/ucp/client/version.rb +1 -1
- data/lib/portage/ucp/client.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 89837fec9676ad52dd925e44ea86b12df99ae2f01ea56eccd4b877bbde7dfb7a
|
|
4
|
+
data.tar.gz: 34e0f987ba86fb975eaed185950cc6c2690c3f177ae8eacbbdb718560be4fb5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 99581702b0ff34ed5a8fd680ffac05a05576823a6381bf312b63436107fb55977be865f7649be32827b3503b28465e3a862df9730058fe7ba4033179acd28505
|
|
7
|
+
data.tar.gz: db7d14a2a8149ca8b526ab4fc0ee6f6f807cad84b83eb57229879659e75e5d13207f96faada575354da1981fb78bc2a1de72edc98c38405a81a5efc3ddcfa7e1
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to this project are documented here. Format loosely follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/); this project is
|
|
5
5
|
pre-1.0, so APIs may still shift between minor versions.
|
|
6
6
|
|
|
7
|
+
## [0.6.2] - 2026-09-23
|
|
8
|
+
|
|
9
|
+
- Fix: the loopback and stdio transports dropped `cart_id` from every call,
|
|
10
|
+
so `get_cart`, `update_cart` and `cancel_cart` always failed with
|
|
11
|
+
"Missing required arguments: cart_id". `cart_id` is a real-UCP wire
|
|
12
|
+
argument only on `create_checkout` (the cart-to-checkout conversion), and
|
|
13
|
+
now only that call drops it. The shared rule is
|
|
14
|
+
`Transports::LocalArguments`.
|
|
15
|
+
- The UCP wire reshaping in `Transports::Http` moved to
|
|
16
|
+
`Transports::UcpWireShape`, so other transports can build the same body.
|
|
17
|
+
`portage-ucp-webmcp` uses it for UCP-shaped page tools. There is no
|
|
18
|
+
behavior change for `Http`.
|
|
19
|
+
|
|
7
20
|
## [0.6.1] - 2026-09-22
|
|
8
21
|
|
|
9
22
|
- `ServerError` carries the parsed error document on `#payload` when the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require_relative "../errors"
|
|
2
|
-
require_relative "
|
|
2
|
+
require_relative "ucp_wire_shape"
|
|
3
3
|
|
|
4
4
|
module Portage
|
|
5
5
|
module Ucp
|
|
@@ -23,18 +23,7 @@ module Portage
|
|
|
23
23
|
# into that real wire format before the request goes out — Loopback
|
|
24
24
|
# and Stdio are untouched.
|
|
25
25
|
class Http
|
|
26
|
-
include
|
|
27
|
-
|
|
28
|
-
# Actions whose one identifying argument becomes a top-level `id`.
|
|
29
|
-
ID_ARG = {
|
|
30
|
-
"get_cart" => :cart_id, "cancel_cart" => :cart_id,
|
|
31
|
-
"get_checkout" => :checkout_id, "cancel_checkout" => :checkout_id,
|
|
32
|
-
"get_order" => :order_id
|
|
33
|
-
}.freeze
|
|
34
|
-
|
|
35
|
-
CATALOG_ACTIONS = %w[get_product lookup_catalog search_catalog].freeze
|
|
36
|
-
CART_ACTIONS = %w[create_cart update_cart].freeze
|
|
37
|
-
CHECKOUT_ACTIONS = %w[create_checkout update_checkout].freeze
|
|
26
|
+
include UcpWireShape
|
|
38
27
|
|
|
39
28
|
def initialize(url:, headers: {})
|
|
40
29
|
@client = ::MCP::Client.new(transport: ::MCP::Client::HTTP.new(url: url, headers: headers))
|
|
@@ -77,93 +66,6 @@ module Portage
|
|
|
77
66
|
wire["idempotency-key"] = idempotency_key if idempotency_key
|
|
78
67
|
wire
|
|
79
68
|
end
|
|
80
|
-
|
|
81
|
-
def wire_arguments(name, arguments)
|
|
82
|
-
idempotency_key = arguments[:idempotency_key] || arguments["idempotency_key"]
|
|
83
|
-
arguments = arguments.dup
|
|
84
|
-
arguments.delete(:idempotency_key)
|
|
85
|
-
|
|
86
|
-
return complete_checkout_body(arguments, idempotency_key) if name == "complete_checkout"
|
|
87
|
-
return { "id" => arguments.fetch(ID_ARG[name]) } if ID_ARG.key?(name)
|
|
88
|
-
return catalog_body(name, arguments) if CATALOG_ACTIONS.include?(name)
|
|
89
|
-
return wrap_line_items("cart", arguments, id_key: :cart_id) if CART_ACTIONS.include?(name)
|
|
90
|
-
return wrap_line_items("checkout", arguments, id_key: :checkout_id) if CHECKOUT_ACTIONS.include?(name)
|
|
91
|
-
|
|
92
|
-
arguments
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def catalog_body(name, arguments)
|
|
96
|
-
body = case name
|
|
97
|
-
when "get_product" then { "id" => arguments.fetch(:product_id) }
|
|
98
|
-
when "lookup_catalog" then { "ids" => arguments.fetch(:product_ids) }
|
|
99
|
-
when "search_catalog" then search_catalog_body(arguments)
|
|
100
|
-
end
|
|
101
|
-
{ "catalog" => with_context(body, arguments) }
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# `context` carries the buyer's locale/currency/region hints the UCP
|
|
105
|
-
# `context` object is specified for. It looks optional and isn't:
|
|
106
|
-
# Shopify resolves which market (and therefore which publication and
|
|
107
|
-
# inventory) a call is scoped to from it, and a cart built without it
|
|
108
|
-
# comes back with `line_items: []`, zeroed totals, and a
|
|
109
|
-
# `merchandise_out_of_stock` warning naming a product `search_catalog`
|
|
110
|
-
# had just reported as `availability.available == true` on the same
|
|
111
|
-
# store. Confirmed live 2026-09-22: identical `create_cart`, context
|
|
112
|
-
# added, returns the line item at its real price.
|
|
113
|
-
#
|
|
114
|
-
# A sweep of nine third-party Shopify stores the same day found the
|
|
115
|
-
# empty cart is only the loudest of three outcomes for an omitted
|
|
116
|
-
# context. Three stores built a correct cart without one; one
|
|
117
|
-
# emptied it; five priced it in the market of the *caller's IP*
|
|
118
|
-
# (a run from Bangkok got THB totals from stores whose shoppers had
|
|
119
|
-
# asked for nothing of the sort). So an omitted context doesn't
|
|
120
|
-
# merely degrade results — it produces a valid-shaped cart at the
|
|
121
|
-
# wrong currency, with nothing on the response saying so, which is
|
|
122
|
-
# the harder failure to notice of the two.
|
|
123
|
-
#
|
|
124
|
-
# Sending one is necessary, not sufficient: the cart is scoped to
|
|
125
|
-
# the market the context names, so a product the store doesn't
|
|
126
|
-
# publish into that market drops out with the same
|
|
127
|
-
# `merchandise_out_of_stock` message (mejuri.com, `US`/`USD` and
|
|
128
|
-
# `GB`/`GBP` empty, `CA`/`CAD` fine). That store's `search_catalog`
|
|
129
|
-
# ignored the context entirely and quoted CAD at
|
|
130
|
-
# `available: true` for every market asked, so the search result
|
|
131
|
-
# gives a caller no warning. Nothing to fix here — it's the store's
|
|
132
|
-
# market config — but callers shouldn't read this code as a
|
|
133
|
-
# guarantee that a context makes carts work.
|
|
134
|
-
def with_context(body, arguments)
|
|
135
|
-
context = arguments[:context] || arguments["context"]
|
|
136
|
-
return body if context.nil? || context.empty?
|
|
137
|
-
|
|
138
|
-
body.merge("context" => stringify(context))
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def stringify(context)
|
|
142
|
-
context.to_h { |key, value| [key.to_s, value] }
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def search_catalog_body(arguments)
|
|
146
|
-
{ "query" => arguments[:query], "pagination" => { "limit" => arguments[:limit] }.compact }.compact
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# `cart_id` on a checkout body is the cart→checkout conversion the
|
|
150
|
-
# spec's `checkout.cart_id` describes ("the business uses cart
|
|
151
|
-
# contents and ignores overlapping fields"). Its schema says
|
|
152
|
-
# `cart_id` alone is enough; the live server disagrees and rejects
|
|
153
|
-
# that with `Invalid arguments: object at '/checkout' is missing
|
|
154
|
-
# required properties: line_items` (confirmed live 2026-09-22), so
|
|
155
|
-
# the line items go out alongside it rather than instead of it.
|
|
156
|
-
def wrap_line_items(wrapper, arguments, id_key:)
|
|
157
|
-
body = { "line_items" => Array(arguments[:line_items]).map { |li| wire_line_item(li) } }
|
|
158
|
-
body["cart_id"] = arguments[:cart_id] if wrapper == "checkout" && arguments[:cart_id]
|
|
159
|
-
body = with_context(body, arguments)
|
|
160
|
-
{ wrapper => body }.tap { |h| h["id"] = arguments[id_key] if arguments[id_key] }
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def wire_line_item(line_item)
|
|
164
|
-
{ "item" => { "id" => line_item[:product_id] || line_item["product_id"] },
|
|
165
|
-
"quantity" => line_item[:quantity] || line_item["quantity"] }
|
|
166
|
-
end
|
|
167
69
|
end
|
|
168
70
|
end
|
|
169
71
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Portage
|
|
2
|
+
module Ucp
|
|
3
|
+
module Client
|
|
4
|
+
module Transports
|
|
5
|
+
# Session's arguments trimmed to what this gem's own flat-argument
|
|
6
|
+
# server (Portage::Ucp::Mcp::Server -> Dispatcher -> Adapter) takes.
|
|
7
|
+
# `context`/`cart_id`/`handler_id`/`credential_type` are real-UCP
|
|
8
|
+
# wire concerns Session offers for Transports::Http to nest into a
|
|
9
|
+
# request body; the Adapter method signatures have no such keywords,
|
|
10
|
+
# so passing them on would be an ArgumentError on every call.
|
|
11
|
+
#
|
|
12
|
+
# `cart_id` is the exception that needs care: it is only a wire
|
|
13
|
+
# concern on create_checkout (the cart->checkout conversion). On
|
|
14
|
+
# get_cart/update_cart/cancel_cart it *is* the Adapter's own keyword,
|
|
15
|
+
# and dropping it there made every cart read/update/cancel fail with
|
|
16
|
+
# "Missing required arguments: cart_id".
|
|
17
|
+
module LocalArguments
|
|
18
|
+
REMOTE_WIRE_ARGUMENTS = %i[context cart_id handler_id credential_type].freeze
|
|
19
|
+
CART_ID_IS_WIRE_ONLY = %w[create_checkout].freeze
|
|
20
|
+
|
|
21
|
+
def self.strip(name, arguments)
|
|
22
|
+
remote = REMOTE_WIRE_ARGUMENTS
|
|
23
|
+
remote -= [:cart_id] unless CART_ID_IS_WIRE_ONLY.include?(name.to_s)
|
|
24
|
+
arguments.except(*remote)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -15,19 +15,15 @@ module Portage
|
|
|
15
15
|
@next_id = 0
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# gem's own Dispatcher, which splats them into an Adapter method
|
|
22
|
-
# signature that has no such keywords, so passing them on would be
|
|
23
|
-
# an ArgumentError on every call. Dropped here rather than branched
|
|
24
|
-
# on in Session, so each transport keeps owning which arguments it
|
|
18
|
+
# Real-UCP wire concerns this transport drops before they reach the
|
|
19
|
+
# Adapter — see LocalArguments. Dropped here rather than branched on
|
|
20
|
+
# in Session, so each transport keeps owning which arguments it
|
|
25
21
|
# understands.
|
|
26
|
-
REMOTE_WIRE_ARGUMENTS =
|
|
22
|
+
REMOTE_WIRE_ARGUMENTS = LocalArguments::REMOTE_WIRE_ARGUMENTS
|
|
27
23
|
|
|
28
24
|
def call_tool(name:, arguments:, meta: nil)
|
|
29
25
|
@next_id += 1
|
|
30
|
-
arguments =
|
|
26
|
+
arguments = LocalArguments.strip(name, arguments)
|
|
31
27
|
response = @server.handle(
|
|
32
28
|
{ jsonrpc: "2.0", id: @next_id, method: "tools/call",
|
|
33
29
|
params: { name: name, arguments: arguments, **(meta ? { _meta: wire_meta(meta) } : {}) } }
|
|
@@ -12,18 +12,14 @@ module Portage
|
|
|
12
12
|
@client.connect
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# gem's own Dispatcher, which splats them into an Adapter method
|
|
19
|
-
# signature that has no such keywords, so passing them on would be
|
|
20
|
-
# an ArgumentError on every call. Dropped here rather than branched
|
|
21
|
-
# on in Session, so each transport keeps owning which arguments it
|
|
15
|
+
# Real-UCP wire concerns this transport drops before they reach the
|
|
16
|
+
# Adapter — see LocalArguments. Dropped here rather than branched on
|
|
17
|
+
# in Session, so each transport keeps owning which arguments it
|
|
22
18
|
# understands.
|
|
23
|
-
REMOTE_WIRE_ARGUMENTS =
|
|
19
|
+
REMOTE_WIRE_ARGUMENTS = LocalArguments::REMOTE_WIRE_ARGUMENTS
|
|
24
20
|
|
|
25
21
|
def call_tool(name:, arguments:, meta: nil)
|
|
26
|
-
response = @client.call_tool(name: name, arguments:
|
|
22
|
+
response = @client.call_tool(name: name, arguments: LocalArguments.strip(name, arguments), meta: meta)
|
|
27
23
|
ToolResult.extract(response, symbol_keys: false)
|
|
28
24
|
end
|
|
29
25
|
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require_relative "http/complete_checkout_wire_shape"
|
|
2
|
+
|
|
3
|
+
module Portage
|
|
4
|
+
module Ucp
|
|
5
|
+
module Client
|
|
6
|
+
module Transports
|
|
7
|
+
# Session's flat arguments reshaped into the real UCP wire format —
|
|
8
|
+
# every tool nests its arguments under a capability key
|
|
9
|
+
# (catalog:/cart:/checkout:), confirmed live against Shopify's
|
|
10
|
+
# 2026-08-25 rollout (see Transports::Http). Split out of Http so any
|
|
11
|
+
# transport that reaches a UCP-shaped tool surface (Streamable HTTP
|
|
12
|
+
# today, a page's WebMCP tools in portage-ucp-webmcp) builds the same
|
|
13
|
+
# body from the same code rather than a second copy drifting from it.
|
|
14
|
+
# Private instance methods of the including transport, as before.
|
|
15
|
+
module UcpWireShape
|
|
16
|
+
include Http::CompleteCheckoutWireShape
|
|
17
|
+
|
|
18
|
+
# Actions whose one identifying argument becomes a top-level `id`.
|
|
19
|
+
ID_ARG = {
|
|
20
|
+
"get_cart" => :cart_id, "cancel_cart" => :cart_id,
|
|
21
|
+
"get_checkout" => :checkout_id, "cancel_checkout" => :checkout_id,
|
|
22
|
+
"get_order" => :order_id
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
CATALOG_ACTIONS = %w[get_product lookup_catalog search_catalog].freeze
|
|
26
|
+
CART_ACTIONS = %w[create_cart update_cart].freeze
|
|
27
|
+
CHECKOUT_ACTIONS = %w[create_checkout update_checkout].freeze
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def wire_arguments(name, arguments)
|
|
32
|
+
idempotency_key = arguments[:idempotency_key] || arguments["idempotency_key"]
|
|
33
|
+
arguments = arguments.dup
|
|
34
|
+
arguments.delete(:idempotency_key)
|
|
35
|
+
|
|
36
|
+
return complete_checkout_body(arguments, idempotency_key) if name == "complete_checkout"
|
|
37
|
+
return { "id" => arguments.fetch(ID_ARG[name]) } if ID_ARG.key?(name)
|
|
38
|
+
return catalog_body(name, arguments) if CATALOG_ACTIONS.include?(name)
|
|
39
|
+
return wrap_line_items("cart", arguments, id_key: :cart_id) if CART_ACTIONS.include?(name)
|
|
40
|
+
return wrap_line_items("checkout", arguments, id_key: :checkout_id) if CHECKOUT_ACTIONS.include?(name)
|
|
41
|
+
|
|
42
|
+
arguments
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def catalog_body(name, arguments)
|
|
46
|
+
body = case name
|
|
47
|
+
when "get_product" then { "id" => arguments.fetch(:product_id) }
|
|
48
|
+
when "lookup_catalog" then { "ids" => arguments.fetch(:product_ids) }
|
|
49
|
+
when "search_catalog" then search_catalog_body(arguments)
|
|
50
|
+
end
|
|
51
|
+
{ "catalog" => with_context(body, arguments) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# `context` carries the buyer's locale/currency/region hints the UCP
|
|
55
|
+
# `context` object is specified for. It looks optional and isn't:
|
|
56
|
+
# Shopify resolves which market (and therefore which publication and
|
|
57
|
+
# inventory) a call is scoped to from it, and a cart built without it
|
|
58
|
+
# comes back with `line_items: []`, zeroed totals, and a
|
|
59
|
+
# `merchandise_out_of_stock` warning naming a product `search_catalog`
|
|
60
|
+
# had just reported as `availability.available == true` on the same
|
|
61
|
+
# store. Confirmed live 2026-09-22: identical `create_cart`, context
|
|
62
|
+
# added, returns the line item at its real price.
|
|
63
|
+
#
|
|
64
|
+
# A sweep of nine third-party Shopify stores the same day found the
|
|
65
|
+
# empty cart is only the loudest of three outcomes for an omitted
|
|
66
|
+
# context. Three stores built a correct cart without one; one
|
|
67
|
+
# emptied it; five priced it in the market of the *caller's IP*
|
|
68
|
+
# (a run from Bangkok got THB totals from stores whose shoppers had
|
|
69
|
+
# asked for nothing of the sort). So an omitted context doesn't
|
|
70
|
+
# merely degrade results — it produces a valid-shaped cart at the
|
|
71
|
+
# wrong currency, with nothing on the response saying so, which is
|
|
72
|
+
# the harder failure to notice of the two.
|
|
73
|
+
#
|
|
74
|
+
# Sending one is necessary, not sufficient: the cart is scoped to
|
|
75
|
+
# the market the context names, so a product the store doesn't
|
|
76
|
+
# publish into that market drops out with the same
|
|
77
|
+
# `merchandise_out_of_stock` message (mejuri.com, `US`/`USD` and
|
|
78
|
+
# `GB`/`GBP` empty, `CA`/`CAD` fine). That store's `search_catalog`
|
|
79
|
+
# ignored the context entirely and quoted CAD at
|
|
80
|
+
# `available: true` for every market asked, so the search result
|
|
81
|
+
# gives a caller no warning. Nothing to fix here — it's the store's
|
|
82
|
+
# market config — but callers shouldn't read this code as a
|
|
83
|
+
# guarantee that a context makes carts work.
|
|
84
|
+
def with_context(body, arguments)
|
|
85
|
+
context = arguments[:context] || arguments["context"]
|
|
86
|
+
return body if context.nil? || context.empty?
|
|
87
|
+
|
|
88
|
+
body.merge("context" => stringify(context))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def stringify(context)
|
|
92
|
+
context.to_h { |key, value| [key.to_s, value] }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def search_catalog_body(arguments)
|
|
96
|
+
{ "query" => arguments[:query], "pagination" => { "limit" => arguments[:limit] }.compact }.compact
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# `cart_id` on a checkout body is the cart→checkout conversion the
|
|
100
|
+
# spec's `checkout.cart_id` describes ("the business uses cart
|
|
101
|
+
# contents and ignores overlapping fields"). Its schema says
|
|
102
|
+
# `cart_id` alone is enough; the live server disagrees and rejects
|
|
103
|
+
# that with `Invalid arguments: object at '/checkout' is missing
|
|
104
|
+
# required properties: line_items` (confirmed live 2026-09-22), so
|
|
105
|
+
# the line items go out alongside it rather than instead of it.
|
|
106
|
+
def wrap_line_items(wrapper, arguments, id_key:)
|
|
107
|
+
body = { "line_items" => Array(arguments[:line_items]).map { |li| wire_line_item(li) } }
|
|
108
|
+
body["cart_id"] = arguments[:cart_id] if wrapper == "checkout" && arguments[:cart_id]
|
|
109
|
+
body = with_context(body, arguments)
|
|
110
|
+
{ wrapper => body }.tap { |h| h["id"] = arguments[id_key] if arguments[id_key] }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def wire_line_item(line_item)
|
|
114
|
+
{ "item" => { "id" => line_item[:product_id] || line_item["product_id"] },
|
|
115
|
+
"quantity" => line_item[:quantity] || line_item["quantity"] }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/portage/ucp/client.rb
CHANGED
|
@@ -8,6 +8,7 @@ require_relative "client/version"
|
|
|
8
8
|
require_relative "client/errors"
|
|
9
9
|
require_relative "client/tool_result"
|
|
10
10
|
require_relative "client/session"
|
|
11
|
+
require_relative "client/transports/local_arguments"
|
|
11
12
|
require_relative "client/transports/loopback"
|
|
12
13
|
require_relative "client/transports/stdio"
|
|
13
14
|
require_relative "client/transports/http"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: portage-ucp-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tom Whitbread
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -129,8 +129,10 @@ files:
|
|
|
129
129
|
- lib/portage/ucp/client/tool_result.rb
|
|
130
130
|
- lib/portage/ucp/client/transports/http.rb
|
|
131
131
|
- lib/portage/ucp/client/transports/http/complete_checkout_wire_shape.rb
|
|
132
|
+
- lib/portage/ucp/client/transports/local_arguments.rb
|
|
132
133
|
- lib/portage/ucp/client/transports/loopback.rb
|
|
133
134
|
- lib/portage/ucp/client/transports/stdio.rb
|
|
135
|
+
- lib/portage/ucp/client/transports/ucp_wire_shape.rb
|
|
134
136
|
- lib/portage/ucp/client/version.rb
|
|
135
137
|
homepage: https://github.com/tomtom87/Portage/tree/main/portage-ucp-client
|
|
136
138
|
licenses:
|