portage-cli 0.7.0 → 0.7.4

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.
data/lib/portage/cli.rb CHANGED
@@ -2,6 +2,7 @@ require "optparse"
2
2
  require "json"
3
3
 
4
4
  require_relative "cli/version"
5
+ require_relative "cli/user_agent"
5
6
  require_relative "cli/shipping_profile"
6
7
  require_relative "cli/buyer_context"
7
8
  require_relative "cli/catalog_products"
@@ -10,7 +11,12 @@ require_relative "cli/find"
10
11
  require_relative "cli/compare"
11
12
  require_relative "cli/history"
12
13
  require_relative "cli/payment_methods"
14
+ require_relative "cli/proxy_settings"
13
15
  require_relative "cli/doctor"
16
+ require_relative "cli/handoff_reconciler"
17
+ require_relative "cli/handoff_waiter"
18
+ require_relative "cli/reconcile_notifier"
19
+ require_relative "cli/reconcile_notify"
14
20
  require_relative "cli/generate/adapter"
15
21
  require_relative "cli/generate/agent_profile"
16
22
 
@@ -25,6 +31,7 @@ module Portage
25
31
  [--product-id ID] [--yes] [--dry-run]
26
32
  [--auto-open|--no-auto-open] [--notify-webhook URL]
27
33
  [--decision-backend jev|laya] [--min-confidence N] [--json]
34
+ [--wait [--wait-timeout DURATION|off]]
28
35
  portage buy --query "..." [--store URL] [--max-price N] [--limit N] ...
29
36
  portage find --query "..." [--max-price N] [--limit N] [--json]
30
37
  portage compare <url> --product-id ID [--id VALUE ...] [--results N]
@@ -43,28 +50,66 @@ module Portage
43
50
  [--rolling-cap N --rolling-window-seconds N --currency CUR]
44
51
  [--velocity-count N --velocity-window-seconds N]
45
52
  [--allow HOST ...] [--clear-allowlist]
53
+ portage orders reconcile [--checkout ID] [--json]
46
54
  portage doctor [--require FILE] [--adapter CLASS_NAME] [--json]
47
55
  portage configure [--require FILE] [--adapter CLASS_NAME] [--json] (alias for doctor)
48
56
  portage setup [--require FILE] [--adapter CLASS_NAME] [--json] (alias for doctor)
49
57
  portage generate adapter NAME [--dir DIR]
50
58
  portage generate agent-profile [--out FILE] [--key-out FILE] [--rotate]
59
+ portage --version
60
+
61
+ proxy flags (buy/find/compare/doctor/payment enroll):
62
+ [--proxy URL] [--proxy-mode forward|gateway] [--proxy-header "Name: value"]
63
+ [--no-proxy HOSTS] [--proxy-route ROUTE=URL|direct] [--proxy-chain URL,URL,...]
64
+ [--proxy-passthrough HEADER] [--proxy-ca FILE] [--no-env-proxy]
51
65
  USAGE
52
66
 
53
67
  COMMANDS = { "buy" => :run_buy, "find" => :run_find, "compare" => :run_compare,
54
68
  "history" => :run_history, "payment" => :run_payment, "policy" => :run_policy,
55
- "doctor" => :run_doctor, "configure" => :run_doctor, "setup" => :run_doctor,
56
- "generate" => :run_generate }.freeze
69
+ "orders" => :run_orders, "doctor" => :run_doctor, "configure" => :run_doctor,
70
+ "setup" => :run_doctor, "generate" => :run_generate }.freeze
71
+
72
+ VERSION_FLAGS = %w[--version -v version].freeze
57
73
 
58
74
  # @param argv [Array<String>]
59
75
  # @return [Integer] process exit code
60
76
  def self.run(argv)
61
77
  command, *rest = argv
78
+ return run_version if VERSION_FLAGS.include?(command)
62
79
  return send(COMMANDS[command], rest) if COMMANDS.key?(command)
63
80
 
64
81
  warn USAGE
65
82
  1
66
83
  end
67
84
 
85
+ # So a packaged install (Homebrew's `test do` block, `portage doctor`)
86
+ # can confirm which release it's running without hitting the network.
87
+ def self.run_version
88
+ puts VERSION
89
+ 0
90
+ end
91
+ private_class_method :run_version
92
+
93
+ # --- proxy (docs/plans/proxy-support.md Phase 2) ---
94
+
95
+ # Resolves this command's `--proxy*` flags/env/config.json into a
96
+ # ProxyConfig and installs it as the process-wide default every
97
+ # Support::Connection.start call (buy/find/compare/payment/doctor alike)
98
+ # already reads unless it's handed its own `proxy:` — see ProxySettings'
99
+ # own comment for why. A bad flag or a malformed/protected-header
100
+ # config.json surfaces here as a clean message, never a raw core
101
+ # exception.
102
+ # @return [ProxySettings, nil] nil on a config error (already reported).
103
+ def self.apply_proxy_settings(proxy_flags)
104
+ settings = ProxySettings.new(flags: proxy_flags || {})
105
+ Portage::Ucp::Support::ProxyConfig.current = settings.resolve
106
+ settings
107
+ rescue ProxySettings::ConfigError => e
108
+ warn "portage: #{e.message}"
109
+ nil
110
+ end
111
+ private_class_method :apply_proxy_settings
112
+
68
113
  # --- find ---
69
114
 
70
115
  def self.run_find(argv)
@@ -72,6 +117,8 @@ module Portage
72
117
  return 1 unless options
73
118
 
74
119
  json = options.delete(:json)
120
+ return 1 unless apply_proxy_settings(options.delete(:proxy))
121
+
75
122
  report = Find.new(**options).call
76
123
  record_find(report)
77
124
  puts json ? JSON.pretty_generate(report) : format_find(report)
@@ -98,11 +145,13 @@ module Portage
98
145
  private_class_method :parse_find_options
99
146
 
100
147
  def self.find_option_parser(opts)
148
+ opts[:proxy] = {}
101
149
  OptionParser.new do |parser|
102
150
  parser.on("--query QUERY") { |v| opts[:query] = v }
103
151
  parser.on("--limit N", Integer) { |v| opts[:limit] = v }
104
152
  parser.on("--max-price N", Float) { |v| opts[:max_price] = to_minor_units(v) }
105
153
  parser.on("--json") { opts[:json] = true }
154
+ ProxySettings.add_options(parser, opts[:proxy])
106
155
  end
107
156
  end
108
157
  private_class_method :find_option_parser
@@ -121,6 +170,8 @@ module Portage
121
170
 
122
171
  json = options.delete(:json)
123
172
  url = options.delete(:url)
173
+ return 1 unless apply_proxy_settings(options.delete(:proxy))
174
+
124
175
  report = Compare.new(origin_url: url, **options).call
125
176
  # Recorded as a search, not a purchase — compare never checks out. The
126
177
  # query string names the compare so `portage history list` doesn't
@@ -147,12 +198,14 @@ module Portage
147
198
  private_class_method :parse_compare_options
148
199
 
149
200
  def self.compare_option_parser(opts)
201
+ opts[:proxy] = {}
150
202
  OptionParser.new do |parser|
151
203
  parser.on("--product-id ID") { |v| opts[:origin_product_id] = v }
152
204
  parser.on("--id VALUE") { |v| opts[:identity] << v }
153
205
  parser.on("--results N", Integer) { |v| opts[:results] = v }
154
206
  parser.on("--max-price N", Float) { |v| opts[:max_price] = to_minor_units(v) }
155
207
  parser.on("--json") { opts[:json] = true }
208
+ ProxySettings.add_options(parser, opts[:proxy])
156
209
  end
157
210
  end
158
211
  private_class_method :compare_option_parser
@@ -162,6 +215,7 @@ module Portage
162
215
  def self.run_buy(argv)
163
216
  parsed = parse_buy_options(argv)
164
217
  return 1 unless parsed
218
+ return 1 unless apply_proxy_settings(parsed[:proxy])
165
219
 
166
220
  url = parsed[:buy][:url] || parsed[:store]
167
221
  parsed[:confidence_check] = confidence_check(parsed, url)
@@ -210,11 +264,88 @@ module Portage
210
264
  options[:product_id] ||= product_id
211
265
  report = Buy.new(**options).call
212
266
  record_buy(report, options[:query])
213
- puts parsed[:json] ? JSON.pretty_generate(report) : format_report(report)
267
+ result = parsed[:wait] ? wait_for_handoff(report, parsed) : nil
268
+ print_buy_report(report, result, parsed[:json])
214
269
  report[:checkout] || report[:browse] ? 0 : 1
215
270
  end
216
271
  private_class_method :execute_buy
217
272
 
273
+ # docs/plans/handoff-reconcile.md Phase 3 — `portage buy --wait`. A
274
+ # no-op (returns nil) whenever there's nothing to wait on: --dry-run
275
+ # never hands off at all, and a completed/browse-only/dead-end report
276
+ # has no pending shopper record either. Otherwise polls
277
+ # `HandoffReconciler` through `HandoffWaiter` until it settles or its
278
+ # deadline passes.
279
+ #
280
+ # Under --json, every event streams to stdout as it happens (NDJSON) —
281
+ # the initial hand-off, then a line per checkout-status change, then the
282
+ # settle, so a calling agent reading stdout live never has to guess
283
+ # whether it's still waiting. Under plain output, nothing streams here:
284
+ # the forced "terminal" notify channel (see #wait_notifier) prints its
285
+ # own line when it settles, and the final report follows exactly as it
286
+ # does without --wait.
287
+ def self.wait_for_handoff(report, parsed)
288
+ return nil unless report[:handoff] && report[:checkout_id]
289
+
290
+ transaction_log = Portage::Ucp::Support::TransactionLog.new
291
+ record = pending_shopper_record(report[:checkout_id], transaction_log)
292
+ return nil unless record
293
+
294
+ json = parsed[:json]
295
+ emit_ndjson(handoff_event(report)) if json
296
+ reconciler = HandoffReconciler.new(transaction_log: transaction_log, notifier: wait_notifier(json))
297
+ waiter = HandoffWaiter.new(reconciler: reconciler, transaction_log: transaction_log,
298
+ wait_timeout_override: parsed[:wait_timeout])
299
+ waiter.call(record) { |event, result| emit_wait_event(event, result, json) }
300
+ end
301
+ private_class_method :wait_for_handoff
302
+
303
+ def self.pending_shopper_record(checkout_id, transaction_log)
304
+ transaction_log.each_record.find { |r| r["checkout_id"] == checkout_id && r["settled_by"] == "shopper" }
305
+ end
306
+ private_class_method :pending_shopper_record
307
+
308
+ # `terminal` is forced on for a plain-text wait (see ReconcileNotify) so
309
+ # the shopper sees a line the moment it settles even with nothing
310
+ # configured. Never forced under --json: that channel prints plain text,
311
+ # which would corrupt the NDJSON stream this method's caller is also
312
+ # writing to the same stdout.
313
+ def self.wait_notifier(json)
314
+ ReconcileNotifier.new(channels: ReconcileNotify.resolve(extra: json ? [] : ["terminal"]))
315
+ end
316
+ private_class_method :wait_notifier
317
+
318
+ def self.handoff_event(report)
319
+ { event: "handoff", checkout_id: report[:checkout_id], checkout_url: report[:checkout_url],
320
+ reason: report[:outcome] }
321
+ end
322
+ private_class_method :handoff_event
323
+
324
+ # Only `:settled` ever reaches stdout as `handoff_settled` — a timeout or
325
+ # an interrupted wait leaves `result.settled` false, and that's already
326
+ # visible in the final report object this event stream ends with, so a
327
+ # second, redundant "gave up" event isn't needed.
328
+ def self.emit_wait_event(event, result, json)
329
+ return unless json
330
+
331
+ case event
332
+ when :status then emit_ndjson({ event: "handoff_status", status: result.checkout_status })
333
+ when :settled
334
+ emit_ndjson({ event: "handoff_settled", result: result.status, resolution: result.resolution,
335
+ order_id: result.order_id, amount: result.amount, currency: result.currency }.compact)
336
+ end
337
+ end
338
+ private_class_method :emit_wait_event
339
+
340
+ def self.emit_ndjson(payload) = puts JSON.generate(payload)
341
+ private_class_method :emit_ndjson
342
+
343
+ def self.print_buy_report(report, result, json)
344
+ report = report.merge(reconcile: result.to_h) if result
345
+ puts json ? JSON.pretty_generate(report) : format_report(report)
346
+ end
347
+ private_class_method :print_buy_report
348
+
218
349
  # Built before the buy starts (and before the search, when there's no
219
350
  # URL), so a bad --min-confidence, or a bad PORTAGE_MIN_CONFIDENCE with
220
351
  # a backend enabled, stops the run up front rather than after a
@@ -276,7 +407,10 @@ module Portage
276
407
  buy = { url: url, qty: 1, yes: false, dry_run: false }
277
408
  parsed = { buy: buy, find: {}, confidence: {} }
278
409
  json = argv.include?("--json")
279
- buy_option_parser(buy, parsed).parse!(argv)
410
+ parsed[:proxy] = {}
411
+ parser = buy_option_parser(buy, parsed)
412
+ ProxySettings.add_options(parser, parsed[:proxy])
413
+ parser.parse!(argv)
280
414
  buy[:query] ||= ""
281
415
  return parsed if url || !buy[:query].strip.empty?
282
416
 
@@ -294,9 +428,9 @@ module Portage
294
428
  parser.on("--product-id ID") { |v| buy[:product_id] = v }
295
429
  parser.on("--yes") { buy[:yes] = true }
296
430
  parser.on("--dry-run") { buy[:dry_run] = true }
297
- parser.on("--[no-]auto-open") { |v| buy[:auto_open] = v }
298
- parser.on("--notify-webhook URL") { |v| buy[:notify_webhook] = v }
299
431
  parser.on("--json") { parsed[:json] = true }
432
+ add_handoff_options(parser, buy)
433
+ add_wait_options(parser, parsed)
300
434
  add_search_options(parser, buy, parsed)
301
435
  add_confidence_options(parser, parsed[:confidence])
302
436
  end
@@ -311,6 +445,22 @@ module Portage
311
445
  end
312
446
  private_class_method :add_confidence_options
313
447
 
448
+ def self.add_handoff_options(parser, buy)
449
+ parser.on("--[no-]auto-open") { |v| buy[:auto_open] = v }
450
+ parser.on("--notify-webhook URL") { |v| buy[:notify_webhook] = v }
451
+ end
452
+ private_class_method :add_handoff_options
453
+
454
+ # docs/plans/handoff-reconcile.md Phase 3 — `--wait`/`--wait-timeout`
455
+ # land on `parsed`, never on `buy`: they're consumed by
456
+ # `#wait_for_handoff` after `Buy#call` returns, and `Buy.new` has no
457
+ # `wait:`/`wait_timeout:` keyword to accidentally receive them.
458
+ def self.add_wait_options(parser, parsed)
459
+ parser.on("--wait") { parsed[:wait] = true }
460
+ parser.on("--wait-timeout DURATION") { |v| parsed[:wait_timeout] = v }
461
+ end
462
+ private_class_method :add_wait_options
463
+
314
464
  # `--query` feeds both halves: it's the store search when there's no URL
315
465
  # and the catalog search once a store is settled, so it's registered once
316
466
  # here rather than twice on the same parser.
@@ -318,7 +468,7 @@ module Portage
318
468
  parser.on("--query QUERY") { |v| parsed[:find][:query] = buy[:query] = v }
319
469
  parser.on("--store URL") { |v| parsed[:store] = v }
320
470
  parser.on("--limit N", Integer) { |v| parsed[:find][:limit] = v }
321
- parser.on("--max-price N", Float) { |v| parsed[:find][:max_price] = to_minor_units(v) }
471
+ parser.on("--max-price N", Float) { |v| parsed[:find][:max_price] = buy[:max_price] = to_minor_units(v) }
322
472
  end
323
473
  private_class_method :add_search_options
324
474
 
@@ -455,13 +605,14 @@ module Portage
455
605
  private_class_method :run_payment_mutate
456
606
 
457
607
  def self.parse_payment_enroll_options(argv)
458
- opts = { scope_merchants: [] }
608
+ opts = { scope_merchants: [], proxy: {} }
459
609
  OptionParser.new do |parser|
460
610
  parser.on("--label NAME") { |v| opts[:label] = v }
461
611
  parser.on("--json") { opts[:json] = true }
462
612
  parser.on("--scope-merchant HOST") { |v| opts[:scope_merchants] << v }
463
613
  parser.on("--scope-max-amount N", Integer) { |v| opts[:scope_max_amount] = v }
464
614
  parser.on("--scope-currency CUR") { |v| opts[:scope_currency] = v }
615
+ ProxySettings.add_options(parser, opts[:proxy])
465
616
  end.parse!(argv)
466
617
  opts[:url] = argv.first && !argv.first.start_with?("-") ? argv.shift : nil
467
618
  opts
@@ -486,6 +637,7 @@ module Portage
486
637
  warn USAGE
487
638
  return 1
488
639
  end
640
+ return 1 unless apply_proxy_settings(opts[:proxy])
489
641
 
490
642
  result = PaymentMethods.new.enroll(opts[:url], label: opts[:label],
491
643
  scope: payment_enroll_scope(opts)) do |setup_url|
@@ -610,14 +762,86 @@ module Portage
610
762
  end
611
763
  private_class_method :format_payment_enroll
612
764
 
765
+ # --- orders ---
766
+
767
+ # docs/plans/handoff-reconcile.md Phase 1 — resolves every pending
768
+ # `settled_by: "shopper"` TransactionLog record (or just the one named
769
+ # by `--checkout`) by re-fetching that checkout from the store. Safe to
770
+ # run from cron/launchd: a record that's already terminal, or that
771
+ # doesn't belong to this run (a `--checkout` for a different id, or
772
+ # `settled_by: nil` dispatcher crash-evidence), is a no-op result, not
773
+ # an error, so a scheduled run never needs its own filtering logic.
774
+ def self.run_orders(argv)
775
+ sub = argv.first && !argv.first.start_with?("-") ? argv.shift : nil
776
+ return run_orders_reconcile(argv) if sub == "reconcile"
777
+
778
+ warn USAGE
779
+ 1
780
+ end
781
+ private_class_method :run_orders
782
+
783
+ def self.run_orders_reconcile(argv)
784
+ opts = {}
785
+ OptionParser.new do |parser|
786
+ parser.on("--checkout ID") { |v| opts[:checkout] = v }
787
+ parser.on("--json") { opts[:json] = true }
788
+ end.parse!(argv)
789
+
790
+ transaction_log = Portage::Ucp::Support::TransactionLog.new
791
+ reconciler = HandoffReconciler.new(transaction_log: transaction_log)
792
+ results = reconcile_records(opts[:checkout], transaction_log, reconciler)
793
+
794
+ puts opts[:json] ? JSON.pretty_generate(results.map(&:to_h)) : format_reconcile(results)
795
+ 0
796
+ end
797
+ private_class_method :run_orders_reconcile
798
+
799
+ def self.reconcile_records(checkout_id, transaction_log, reconciler)
800
+ return reconcile_one_checkout(checkout_id, transaction_log, reconciler) if checkout_id
801
+
802
+ HandoffReconciler.each_pending_shopper_record(transaction_log).map { |record| reconciler.call(record) }
803
+ end
804
+ private_class_method :reconcile_records
805
+
806
+ # `--checkout ID` reconciles the one named record whatever its
807
+ # `settled_by`/status — #call itself still refuses to settle anything
808
+ # that isn't a pending shopper record, this just skips the "iterate
809
+ # every pending record" step when the caller already knows which one.
810
+ def self.reconcile_one_checkout(checkout_id, transaction_log, reconciler)
811
+ key = transaction_log.each_record.find { |r| r["checkout_id"] == checkout_id }&.fetch("idempotency_key", nil)
812
+ return [] unless key
813
+
814
+ [reconciler.call(transaction_log.find(key))]
815
+ end
816
+ private_class_method :reconcile_one_checkout
817
+
818
+ def self.format_reconcile(results)
819
+ return "(nothing to reconcile)" if results.empty?
820
+
821
+ results.map { |r| format_reconcile_result(r) }.join("\n")
822
+ end
823
+ private_class_method :format_reconcile
824
+
825
+ def self.format_reconcile_result(result)
826
+ return "#{result.idempotency_key}: #{result.note}" unless result.settled
827
+
828
+ parts = ["#{result.idempotency_key}: #{result.status}"]
829
+ parts << "resolution: #{result.resolution}" if result.resolution
830
+ parts << "order: #{result.order_id}" if result.order_id
831
+ parts << format_amount(result.amount, result.currency) if result.amount
832
+ parts.join(" — ")
833
+ end
834
+ private_class_method :format_reconcile_result
835
+
613
836
  # --- doctor ---
614
837
 
615
838
  def self.parse_doctor_options(argv)
616
- opts = {}
839
+ opts = { proxy: {} }
617
840
  OptionParser.new do |parser|
618
841
  parser.on("--require FILE") { |v| opts[:require] = v }
619
842
  parser.on("--adapter CLASS_NAME") { |v| opts[:adapter] = v }
620
843
  parser.on("--json") { opts[:json] = true }
844
+ ProxySettings.add_options(parser, opts[:proxy])
621
845
  end.parse!(argv)
622
846
  opts
623
847
  end
@@ -627,17 +851,27 @@ module Portage
627
851
  opts = parse_doctor_options(argv)
628
852
  require File.expand_path(opts[:require]) if opts[:require]
629
853
  adapter_class = opts[:adapter] && Object.const_get(opts[:adapter])
854
+ proxy_settings = apply_proxy_settings(opts[:proxy])
855
+ return 1 unless proxy_settings
630
856
 
631
- findings = Doctor.new(adapter_class: adapter_class).call
632
- puts opts[:json] ? JSON.pretty_generate(findings.map(&:to_h)) : format_doctor(findings)
633
- findings.empty? ? 0 : 1
857
+ report_doctor(Doctor.new(adapter_class: adapter_class, proxy_settings: proxy_settings).call, json: opts[:json])
634
858
  end
635
859
  private_class_method :run_doctor
636
860
 
637
- def self.format_doctor(findings)
638
- return "No issues found." if findings.empty?
861
+ def self.report_doctor(findings, json:)
862
+ puts json ? JSON.pretty_generate(findings.map(&:to_h)) : format_doctor(findings)
863
+ findings.none?(&:warning?) ? 0 : 1
864
+ end
865
+ private_class_method :report_doctor
639
866
 
640
- findings.map { |f| "[#{f.check}] #{f.message}" }.join("\n")
867
+ # Info findings (install method, Ruby, adapters, PATH) first, then the
868
+ # warnings, which alone decide the exit code.
869
+ def self.format_doctor(findings)
870
+ info, warnings = findings.partition { |f| !f.warning? }
871
+ lines = info.map { |f| "[#{f.check}] #{f.message}" }
872
+ lines << "" unless info.empty?
873
+ lines.concat(warnings.empty? ? ["No issues found."] : warnings.map { |f| "[#{f.check}] #{f.message}" })
874
+ lines.join("\n")
641
875
  end
642
876
  private_class_method :format_doctor
643
877
 
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: portage-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.4
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-09-23 00:00:00.000000000 Z
11
+ date: 2026-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: irb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: portage-ucp
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '0.9'
33
+ version: '0.10'
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '0.9'
40
+ version: '0.10'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: portage-ucp-client
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -33,7 +47,7 @@ dependencies:
33
47
  version: '0.6'
34
48
  - - ">="
35
49
  - !ruby/object:Gem::Version
36
- version: 0.6.2
50
+ version: 0.6.3
37
51
  type: :runtime
38
52
  prerelease: false
39
53
  version_requirements: !ruby/object:Gem::Requirement
@@ -43,7 +57,7 @@ dependencies:
43
57
  version: '0.6'
44
58
  - - ">="
45
59
  - !ruby/object:Gem::Version
46
- version: 0.6.2
60
+ version: 0.6.3
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: portage-ucp-journal
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -154,17 +168,34 @@ files:
154
168
  - lib/portage/cli/find.rb
155
169
  - lib/portage/cli/generate/adapter.rb
156
170
  - lib/portage/cli/generate/agent_profile.rb
171
+ - lib/portage/cli/handoff_reconciler.rb
172
+ - lib/portage/cli/handoff_reconciler/wire_adapters.rb
173
+ - lib/portage/cli/handoff_spend_mode.rb
174
+ - lib/portage/cli/handoff_wait_timeout.rb
175
+ - lib/portage/cli/handoff_waiter.rb
157
176
  - lib/portage/cli/history.rb
177
+ - lib/portage/cli/homepage_fetch.rb
178
+ - lib/portage/cli/install_doctor.rb
179
+ - lib/portage/cli/macos_notifier.rb
158
180
  - lib/portage/cli/notifier.rb
159
181
  - lib/portage/cli/payment_methods.rb
160
182
  - lib/portage/cli/payment_methods/env_backend.rb
161
183
  - lib/portage/cli/payment_methods/keychain_backend.rb
162
184
  - lib/portage/cli/payment_methods/secret_service_backend.rb
185
+ - lib/portage/cli/permissive_authenticator.rb
163
186
  - lib/portage/cli/probe_cache.rb
187
+ - lib/portage/cli/proxy_doctor.rb
188
+ - lib/portage/cli/proxy_settings.rb
189
+ - lib/portage/cli/proxy_settings/password_ref.rb
190
+ - lib/portage/cli/reconcile_notifier.rb
191
+ - lib/portage/cli/reconcile_notify.rb
164
192
  - lib/portage/cli/search_backends.rb
165
193
  - lib/portage/cli/setting.rb
166
194
  - lib/portage/cli/shipping_profile.rb
195
+ - lib/portage/cli/user_agent.rb
167
196
  - lib/portage/cli/version.rb
197
+ - lib/portage/cli/webmcp.rb
198
+ - lib/portage/cli/webmcp_checkout_mode.rb
168
199
  homepage: https://github.com/tomtom87/Portage/tree/main/portage-cli
169
200
  licenses:
170
201
  - MIT