portage-cli 0.11.0 → 0.13.0

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -1
  3. data/README.md +88 -18
  4. data/lib/portage/cli/adapter_session.rb +34 -0
  5. data/lib/portage/cli/browser_import/importer.rb +1 -1
  6. data/lib/portage/cli/browser_import/plist_xml.rb +5 -12
  7. data/lib/portage/cli/browser_import/sqlite.rb +19 -25
  8. data/lib/portage/cli/browser_profile/cdp.rb +0 -9
  9. data/lib/portage/cli/buy.rb +217 -39
  10. data/lib/portage/cli/compare.rb +4 -3
  11. data/lib/portage/cli/confidence_check.rb +27 -7
  12. data/lib/portage/cli/confidence_state.rb +109 -0
  13. data/lib/portage/cli/find.rb +66 -9
  14. data/lib/portage/cli/generate/agent_profile.rb +1 -1
  15. data/lib/portage/cli/handoff_agents.rb +7 -21
  16. data/lib/portage/cli/handoff_reconciler.rb +2 -22
  17. data/lib/portage/cli/homepage_fetch.rb +5 -3
  18. data/lib/portage/cli/index/builder.rb +2 -11
  19. data/lib/portage/cli/index/known_cache.rb +9 -2
  20. data/lib/portage/cli/index/sources/stores_file.rb +2 -11
  21. data/lib/portage/cli/index/sources/wikidata.rb +2 -11
  22. data/lib/portage/cli/install_doctor.rb +2 -1
  23. data/lib/portage/cli/notifier.rb +16 -12
  24. data/lib/portage/cli/offer_sources.rb +3 -14
  25. data/lib/portage/cli/payment_methods.rb +7 -30
  26. data/lib/portage/cli/reconcile_notifier.rb +2 -5
  27. data/lib/portage/cli/setup_wizard/steps/env_keys_step.rb +40 -0
  28. data/lib/portage/cli/setup_wizard/steps/retailer_keys.rb +11 -23
  29. data/lib/portage/cli/setup_wizard/steps/search_keys.rb +10 -22
  30. data/lib/portage/cli/setup_wizard/steps/shipping.rb +5 -19
  31. data/lib/portage/cli/version.rb +1 -1
  32. data/lib/portage/cli.rb +25 -173
  33. metadata +5 -3
  34. data/lib/portage/cli/known_stores_url.rb +0 -15
@@ -1,4 +1,4 @@
1
- require_relative "../../dot_env"
1
+ require_relative "env_keys_step"
2
2
 
3
3
  module Portage
4
4
  module Cli
@@ -12,7 +12,7 @@ module Portage
12
12
  # setting a key here only makes `find` show more real offers, it
13
13
  # never lets `portage buy` complete a purchase at any of these
14
14
  # retailers.
15
- class RetailerKeys
15
+ class RetailerKeys < EnvKeysStep
16
16
  FIELDS = {
17
17
  "WALMART_AFFILIATE_API_KEY" => "Walmart Affiliate API key",
18
18
  "EBAY_BROWSE_ACCESS_TOKEN" => "eBay Browse API access token (Buy It Now search only)",
@@ -21,33 +21,21 @@ module Portage
21
21
  "AMAZON_CREATORS_ACCESS_TOKEN" => "Amazon Creators API access token"
22
22
  }.freeze
23
23
 
24
- def initialize(prompt:) = @prompt = prompt
25
-
26
24
  def title = "Retailer offer sources"
27
25
  def default_yes? = false
28
26
 
29
- def call
30
- @prompt.say("Optional: official buyer-side APIs for Walmart, eBay (Buy It Now only), Best " \
31
- "Buy, Etsy and Amazon. Each needs its own key from that retailer's developer " \
32
- "program — skip any you don't have. None of these ever completes a purchase: " \
33
- "every offer they return still ends in hand-off, same as Amazon today. Neither " \
34
- "key is echoed back; Enter keeps whatever's already set.")
35
- assignments = collect
36
- return @prompt.say("Left retailer offer source keys unchanged.") if assignments.empty?
37
-
38
- path = DotEnv.update!(assignments)
39
- @prompt.say("Saved #{assignments.keys.join(', ')} to #{path} (chmod 600).")
40
- end
41
-
42
27
  private
43
28
 
44
- def collect
45
- FIELDS.each_with_object({}) do |(var, label), assignments|
46
- hint = ENV.fetch(var, nil).to_s.empty? ? "not set" : "already set"
47
- answer = @prompt.ask_secret(label, hint: hint)
48
- assignments[var] = answer if answer
49
- end
29
+ def intro
30
+ "Optional: official buyer-side APIs for Walmart, eBay (Buy It Now only), Best " \
31
+ "Buy, Etsy and Amazon. Each needs its own key from that retailer's developer " \
32
+ "program — skip any you don't have. None of these ever completes a purchase: " \
33
+ "every offer they return still ends in hand-off, same as Amazon today. Neither " \
34
+ "key is echoed back; Enter keeps whatever's already set."
50
35
  end
36
+
37
+ def unchanged_message = "Left retailer offer source keys unchanged."
38
+ def secret?(_var) = true
51
39
  end
52
40
  end
53
41
  end
@@ -1,4 +1,4 @@
1
- require_relative "../../dot_env"
1
+ require_relative "env_keys_step"
2
2
 
3
3
  module Portage
4
4
  module Cli
@@ -10,7 +10,7 @@ module Portage
10
10
  # with no URL only ever resolves DuckDuckGo's Instant Answer API,
11
11
  # which is keyless but only ever matches a specific brand/product
12
12
  # name, not an open-ended query ("coffee", "hiking boots").
13
- class SearchKeys
13
+ class SearchKeys < EnvKeysStep
14
14
  FIELDS = {
15
15
  "BRAVE_SEARCH_API_KEY" => "Brave Search API key",
16
16
  "GOOGLE_CSE_KEY" => "Google Programmable Search API key",
@@ -21,32 +21,20 @@ module Portage
21
21
  # only the two API keys are secrets that never echo back.
22
22
  SECRET = %w[BRAVE_SEARCH_API_KEY GOOGLE_CSE_KEY].freeze
23
23
 
24
- def initialize(prompt:) = @prompt = prompt
25
-
26
24
  def title = "Search API keys"
27
25
  def default_yes? = true
28
26
 
29
- def call
30
- @prompt.say("DuckDuckGo's free API is already active and needs no key, but only resolves a " \
31
- "specific brand/product name — set either Brave's key, or both Google fields, for " \
32
- "real open-ended search. Neither key is echoed back; Enter keeps whatever's " \
33
- "already set.")
34
- assignments = collect
35
- return @prompt.say("Left search API keys unchanged.") if assignments.empty?
36
-
37
- path = DotEnv.update!(assignments)
38
- @prompt.say("Saved #{assignments.keys.join(', ')} to #{path} (chmod 600).")
39
- end
40
-
41
27
  private
42
28
 
43
- def collect
44
- FIELDS.each_with_object({}) do |(var, label), assignments|
45
- hint = ENV.fetch(var, nil).to_s.empty? ? "not set" : "already set"
46
- answer = SECRET.include?(var) ? @prompt.ask_secret(label, hint: hint) : @prompt.ask(label, hint: hint)
47
- assignments[var] = answer if answer
48
- end
29
+ def intro
30
+ "DuckDuckGo's free API is already active and needs no key, but only resolves a " \
31
+ "specific brand/product name — set either Brave's key, or both Google fields, for " \
32
+ "real open-ended search. Neither key is echoed back; Enter keeps whatever's " \
33
+ "already set."
49
34
  end
35
+
36
+ def unchanged_message = "Left search API keys unchanged."
37
+ def secret?(var) = SECRET.include?(var)
50
38
  end
51
39
  end
52
40
  end
@@ -1,5 +1,5 @@
1
1
  require_relative "../../shipping_profile"
2
- require_relative "../../dot_env"
2
+ require_relative "env_keys_step"
3
3
 
4
4
  module Portage
5
5
  module Cli
@@ -12,7 +12,7 @@ module Portage
12
12
  # store prices in no market and can report in-stock items as out of
13
13
  # stock (see Doctor#shipping_finding, which this step exists to
14
14
  # resolve).
15
- class Shipping
15
+ class Shipping < EnvKeysStep
16
16
  LABELS = {
17
17
  street_address: "Street address", extended_address: "Apt/suite (optional)",
18
18
  address_locality: "City", address_region: "State/region (optional)",
@@ -21,29 +21,15 @@ module Portage
21
21
  phone_number: "Phone (optional)"
22
22
  }.freeze
23
23
 
24
- def initialize(prompt:) = @prompt = prompt
24
+ FIELDS = ShippingProfile::ENV_VARS.to_h { |key, var| [var, LABELS.fetch(key)] }.freeze
25
25
 
26
26
  def title = "Shipping address"
27
27
  def default_yes? = true
28
28
 
29
- def call
30
- @prompt.say("Enter keeps whatever's already set for a field.")
31
- assignments = collect
32
- return @prompt.say("Left shipping address unchanged.") if assignments.empty?
33
-
34
- path = DotEnv.update!(assignments)
35
- @prompt.say("Saved #{assignments.keys.join(', ')} to #{path} (chmod 600).")
36
- end
37
-
38
29
  private
39
30
 
40
- def collect
41
- ShippingProfile::ENV_VARS.each_with_object({}) do |(key, var), assignments|
42
- hint = ENV.fetch(var, nil).to_s.empty? ? "not set" : "already set"
43
- answer = @prompt.ask(LABELS.fetch(key), hint: hint)
44
- assignments[var] = answer if answer
45
- end
46
- end
31
+ def intro = "Enter keeps whatever's already set for a field."
32
+ def unchanged_message = "Left shipping address unchanged."
47
33
  end
48
34
  end
49
35
  end
@@ -1,5 +1,5 @@
1
1
  module Portage
2
2
  module Cli
3
- VERSION = "0.11.0".freeze
3
+ VERSION = "0.13.0".freeze
4
4
  end
5
5
  end