portage-cli 0.13.0 → 0.13.1
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 +8 -0
- data/lib/portage/cli/buy.rb +11 -2
- data/lib/portage/cli/handoff_reconciler.rb +11 -3
- data/lib/portage/cli/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b9dba6d24f2ebadd03022d53d85684d8cbe8e3110b609f1becadd4d5212ec47b
|
|
4
|
+
data.tar.gz: ed6cfcbb1516e7addd017c25a52343f95c71b72bde869aa7e1acd06a44eb3a48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 54563a08ca407b2847502f2a95290598147f4e78aa47f346ce0f20d637d416afb6e5c7119ba4e19e233fe64286a83cb5b47fc1351d63a7aabac81f5065060893
|
|
7
|
+
data.tar.gz: 396ee2c09e74e5708fd5ade1555c412bf5b342979549b04932809760e0744b303050c0dc92b4a4e97d3e4c8f0090a92f7921cc29b020e277370e2566cdf8ca07
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@ pre-1.0, so APIs may still shift between minor versions.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.13.1] - 2026-10-07
|
|
10
|
+
|
|
11
|
+
- **Fix: `portage orders reconcile` can settle a hand-off on a BigCommerce, Magento, Wix or Shopify-adapter store.** Those adapters kept checkout status in the process that created the checkout, so reconcile (a new process) only ever saw `incomplete` or not-found. With their new releases, an adapter checkout that became a paid order reads as `completed` with its `order`, and reconcile settles it and snapshots the order as it does for native UCP stores. No CLI code changed; a new spec covers the adapter loopback path end to end. WooCommerce has no such signal and still stays pending (design-log §55).
|
|
12
|
+
|
|
13
|
+
- **Fix: `portage orders reconcile` (and `buy --wait`) can read a native UCP checkout again.** `HandoffReconciler` called `get_checkout` and `get_order` without `meta.ucp-agent.profile`, so every real UCP store answered `MissingAgentProfileError`. Reconcile read that as "not found", so a pending hand-off never settled while the checkout lived and would have settled `failed`/`unknown` at expiry even if the shopper had paid. It now sends the same agent profile `buy` does (found in the handoff-reconcile Phase 0 live check, design-log §55). A store error's note is now the server's own message (`ServerError#summary`), not Shopify's whole several-kilobyte UCP envelope.
|
|
14
|
+
|
|
15
|
+
- **Fix: a store that escalates its checkout can be approved and reconciled under `require_approval`.** On a dry run, including the dry run the default `require_approval: any` makes of an unapproved `buy --yes`, a `requires_escalation` checkout was reported as `requires_escalation`, so no quote was saved, there was nothing to `portage approve`, and the report pointed at the checkout link with no pending hand-off recorded. A dry run now reports it as `dry_run` with a new `handoff_expected: true` key and a note that a real run hands it off, so the gated run returns `needs_approval` and the approved run's hand-off is recorded for reconcile (design-log §55). A plain `--yes` run outside the gate still escalates as before.
|
|
16
|
+
|
|
9
17
|
## [0.13.0] - 2026-10-05
|
|
10
18
|
|
|
11
19
|
- **Fix: `payment_methods` adapter fallback no longer silently fails.** `PaymentMethods#adapter_session` referenced `Buy::PermissiveAuthenticator`, which doesn't exist (the class is `Portage::Cli::PermissiveAuthenticator`); the resulting `NameError` was swallowed by the method's `rescue StandardError`, so enrolling against your own adapter-backed store always reported no session. It now requires and uses the right constant. Patch-level bug fix.
|
data/lib/portage/cli/buy.rb
CHANGED
|
@@ -1075,8 +1075,13 @@ module Portage
|
|
|
1075
1075
|
return quote_changed_report(source, products, checkout, warnings) if quote_exceeded?(checkout)
|
|
1076
1076
|
|
|
1077
1077
|
escalation = decide_escalation(checkout, warnings)
|
|
1078
|
+
# A dry run never hands off, so an escalating checkout is still just
|
|
1079
|
+
# a priced dry run: reporting it as `requires_escalation` saved no
|
|
1080
|
+
# quote, and the CLI's approval gate had nothing to approve
|
|
1081
|
+
# (design-log §55). The real run after approval escalates and hands
|
|
1082
|
+
# off, which is where the pending record gets written.
|
|
1083
|
+
return dry_run_report(source, products, checkout, warnings, escalates: escalation[:escalate]) if @dry_run
|
|
1078
1084
|
return escalated_report(source, products, checkout, warnings, escalation) if escalation[:escalate]
|
|
1079
|
-
return dry_run_report(source, products, checkout, warnings) if @dry_run
|
|
1080
1085
|
return webmcp_handoff_checkout_report(source, products, checkout, warnings) if force_handoff
|
|
1081
1086
|
return confirmation_needed_report(source, products, checkout, warnings) unless confirmed?
|
|
1082
1087
|
|
|
@@ -1582,9 +1587,13 @@ module Portage
|
|
|
1582
1587
|
# `warnings` here are #reconcile_checkout's mismatches, which a real
|
|
1583
1588
|
# run of the same checkout stops on — so the report says so up front,
|
|
1584
1589
|
# before anyone approves a quote for it.
|
|
1585
|
-
def dry_run_report(source, products, checkout, warnings = [])
|
|
1590
|
+
def dry_run_report(source, products, checkout, warnings = [], escalates: false)
|
|
1586
1591
|
message = "Dry run — checkout created but not completed."
|
|
1587
1592
|
extra = {}
|
|
1593
|
+
if escalates
|
|
1594
|
+
message += " The store needs the shopper to finish this checkout, so a real run hands it off."
|
|
1595
|
+
extra[:handoff_expected] = true
|
|
1596
|
+
end
|
|
1588
1597
|
if warnings.any?
|
|
1589
1598
|
message += " It doesn't match the request (#{warnings.join(' ')}), so a real purchase would stop " \
|
|
1590
1599
|
"with checkout_mismatch."
|
|
@@ -4,6 +4,7 @@ require "portage/ucp"
|
|
|
4
4
|
require "portage/ucp/client"
|
|
5
5
|
require "portage/ucp/journal"
|
|
6
6
|
require_relative "user_agent"
|
|
7
|
+
require_relative "agent_profile_url"
|
|
7
8
|
require_relative "adapter_session"
|
|
8
9
|
require_relative "handoff_spend_mode"
|
|
9
10
|
require_relative "reconcile_notifier"
|
|
@@ -92,7 +93,7 @@ module Portage
|
|
|
92
93
|
|
|
93
94
|
def reconcile(record)
|
|
94
95
|
session = reconnect(record["store_url"])
|
|
95
|
-
checkout = session.get_checkout(checkout_id: record["checkout_id"])
|
|
96
|
+
checkout = session.get_checkout(checkout_id: record["checkout_id"], meta: agent_meta)
|
|
96
97
|
settle_from_checkout(record, checkout)
|
|
97
98
|
rescue StandardError => e
|
|
98
99
|
# A reconnect failure (no automated path back into the store) and a
|
|
@@ -102,7 +103,9 @@ module Portage
|
|
|
102
103
|
# buys nothing here: either way, this run learned nothing new about
|
|
103
104
|
# whether the shopper paid, and the record's own `expires_at` is
|
|
104
105
|
# still the only thing allowed to turn that into a settle.
|
|
105
|
-
|
|
106
|
+
# ServerError#summary: Shopify answers a gone checkout with its whole
|
|
107
|
+
# UCP envelope as the message; the note only needs its messages[].
|
|
108
|
+
settle_not_found(record, note: e.respond_to?(:summary) ? e.summary : e.message)
|
|
106
109
|
end
|
|
107
110
|
|
|
108
111
|
def settle_from_checkout(record, checkout)
|
|
@@ -176,7 +179,7 @@ module Portage
|
|
|
176
179
|
return nil unless order && order["id"]
|
|
177
180
|
|
|
178
181
|
session = reconnect(record["store_url"])
|
|
179
|
-
order_wire = session.get_order(order_id: order["id"])
|
|
182
|
+
order_wire = session.get_order(order_id: order["id"], meta: agent_meta)
|
|
180
183
|
return order["id"] unless order_wire
|
|
181
184
|
|
|
182
185
|
@order_ledger.record(idempotency_key: record["idempotency_key"], order: WireOrder.new(order_wire))
|
|
@@ -233,6 +236,11 @@ module Portage
|
|
|
233
236
|
raise(ReconnectError, "no automated path back into #{store_url}")
|
|
234
237
|
end
|
|
235
238
|
|
|
239
|
+
# Same `meta.ucp-agent.profile` Buy#agent_meta sends: a real UCP
|
|
240
|
+
# server rejects any call without it, so leaving it off made every
|
|
241
|
+
# native reconcile fail as "not found" (design-log §55).
|
|
242
|
+
def agent_meta = { agent_profile: AgentProfileUrl.resolve }
|
|
243
|
+
|
|
236
244
|
def native_session(uri)
|
|
237
245
|
Portage::Ucp::Client.discover(uri.to_s, headers: UserAgent.headers)
|
|
238
246
|
rescue Portage::Ucp::Client::DiscoveryError
|
data/lib/portage/cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: portage-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.13.
|
|
4
|
+
version: 0.13.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tom Whitbread
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: irb
|