weft-sdk 0.25.0 → 0.32.5

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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/docs/FetchApi.md +3 -3
  3. data/docs/FetchErrorResponse.md +1 -1
  4. data/docs/FetchRequest.md +6 -2
  5. data/docs/FetchResponse.md +5 -3
  6. data/docs/GetPurchaseIdParameter.md +48 -0
  7. data/docs/PaymentsApi.md +2 -0
  8. data/docs/Purchase.md +6 -0
  9. data/docs/PurchasesApi.md +2 -2
  10. data/docs/SearchApi.md +1 -1
  11. data/docs/SearchCompactAccess.md +23 -0
  12. data/docs/SearchCompactAccessPrice.md +23 -0
  13. data/docs/SearchCompactNextCall.md +23 -0
  14. data/docs/SearchCompactResult.md +47 -0
  15. data/docs/SearchCompactResultAttribution.md +21 -0
  16. data/docs/SearchCompactResultBlockedScopesInner.md +19 -0
  17. data/docs/SearchCompactResultOperation.md +21 -0
  18. data/docs/SearchCompactResultProvider.md +19 -0
  19. data/docs/SearchEndpointCall.md +1 -1
  20. data/docs/SearchEndpointCallInputSchema.md +14 -0
  21. data/docs/SearchEndpointHit.md +5 -1
  22. data/docs/SearchHelperDocs.md +23 -0
  23. data/docs/SearchRequest.md +3 -1
  24. data/docs/SearchResponse.md +9 -3
  25. data/docs/SearchWeftFetchCompatibility.md +3 -1
  26. data/docs/WebhooksApi.md +414 -0
  27. data/lib/weft/client.rb +271 -0
  28. data/lib/weft/error.rb +41 -0
  29. data/lib/weft/facilitator/client.rb +254 -42
  30. data/lib/weft/facilitator/echo.rb +99 -0
  31. data/lib/weft/facilitator/extensions.rb +183 -0
  32. data/lib/weft/facilitator/fee.rb +26 -11
  33. data/lib/weft/facilitator/handshake.rb +92 -0
  34. data/lib/weft/facilitator/json_wire.rb +74 -0
  35. data/lib/weft/facilitator/middleware.rb +563 -137
  36. data/lib/weft/facilitator/path_match.rb +179 -0
  37. data/lib/weft/facilitator/product.rb +380 -0
  38. data/lib/weft/facilitator/replay.rb +46 -0
  39. data/lib/weft/facilitator/settlement.rb +117 -0
  40. data/lib/weft/facilitator/x402.rb +355 -0
  41. data/lib/weft/generated/api/account_api.rb +2 -2
  42. data/lib/weft/generated/api/agent_bootstrap_api.rb +2 -2
  43. data/lib/weft/generated/api/api_keys_api.rb +2 -2
  44. data/lib/weft/generated/api/auth_api.rb +2 -2
  45. data/lib/weft/generated/api/balance_api.rb +2 -2
  46. data/lib/weft/generated/api/documentation_api.rb +2 -2
  47. data/lib/weft/generated/api/fetch_api.rb +6 -6
  48. data/lib/weft/generated/api/payments_api.rb +4 -2
  49. data/lib/weft/generated/api/purchases_api.rb +4 -4
  50. data/lib/weft/generated/api/resources_api.rb +2 -2
  51. data/lib/weft/generated/api/search_api.rb +4 -4
  52. data/lib/weft/generated/api/webhooks_api.rb +388 -0
  53. data/lib/weft/generated/api_client.rb +2 -2
  54. data/lib/weft/generated/api_error.rb +2 -2
  55. data/lib/weft/generated/api_model_base.rb +2 -2
  56. data/lib/weft/generated/configuration.rb +2 -2
  57. data/lib/weft/generated/models/account_bootstrap_approval.rb +2 -2
  58. data/lib/weft/generated/models/account_bootstrap_created.rb +2 -2
  59. data/lib/weft/generated/models/account_bootstrap_created_approval.rb +2 -2
  60. data/lib/weft/generated/models/account_bootstrap_created_response.rb +2 -2
  61. data/lib/weft/generated/models/account_bootstrap_request.rb +2 -2
  62. data/lib/weft/generated/models/account_bootstrap_status.rb +2 -2
  63. data/lib/weft/generated/models/account_bootstrap_status_response.rb +2 -2
  64. data/lib/weft/generated/models/account_details.rb +2 -2
  65. data/lib/weft/generated/models/api_key.rb +2 -2
  66. data/lib/weft/generated/models/api_key_created.rb +2 -2
  67. data/lib/weft/generated/models/api_key_created_response.rb +2 -2
  68. data/lib/weft/generated/models/api_key_list_response.rb +2 -2
  69. data/lib/weft/generated/models/auth_response.rb +2 -2
  70. data/lib/weft/generated/models/auth_response_data.rb +2 -2
  71. data/lib/weft/generated/models/balance_response.rb +2 -2
  72. data/lib/weft/generated/models/confirm_request.rb +2 -2
  73. data/lib/weft/generated/models/create_api_key_request.rb +2 -2
  74. data/lib/weft/generated/models/enrolled_resource.rb +2 -2
  75. data/lib/weft/generated/models/error.rb +2 -2
  76. data/lib/weft/generated/models/error_response.rb +2 -2
  77. data/lib/weft/generated/models/fetch403_response.rb +2 -2
  78. data/lib/weft/generated/models/fetch_balance_snapshot.rb +2 -2
  79. data/lib/weft/generated/models/fetch_error_response.rb +5 -5
  80. data/lib/weft/generated/models/fetch_request.rb +46 -5
  81. data/lib/weft/generated/models/fetch_request_body.rb +2 -2
  82. data/lib/weft/generated/models/fetch_response.rb +36 -64
  83. data/lib/weft/generated/models/get_purchase_id_parameter.rb +104 -0
  84. data/lib/weft/generated/models/insufficient_scope_response.rb +2 -2
  85. data/lib/weft/generated/models/me_api_key.rb +2 -2
  86. data/lib/weft/generated/models/me_api_key_creator.rb +2 -2
  87. data/lib/weft/generated/models/me_response.rb +2 -2
  88. data/lib/weft/generated/models/me_response_data.rb +2 -2
  89. data/lib/weft/generated/models/message_response.rb +2 -2
  90. data/lib/weft/generated/models/message_response_data.rb +2 -2
  91. data/lib/weft/generated/models/pagination.rb +2 -2
  92. data/lib/weft/generated/models/password_reset_request.rb +2 -2
  93. data/lib/weft/generated/models/password_update_request.rb +2 -2
  94. data/lib/weft/generated/models/payment.rb +2 -2
  95. data/lib/weft/generated/models/payment_list_response.rb +2 -2
  96. data/lib/weft/generated/models/payment_response.rb +2 -2
  97. data/lib/weft/generated/models/principal_wallet.rb +2 -2
  98. data/lib/weft/generated/models/promo_balance.rb +2 -2
  99. data/lib/weft/generated/models/purchase.rb +75 -3
  100. data/lib/weft/generated/models/purchase_artifact.rb +2 -2
  101. data/lib/weft/generated/models/purchase_list_response.rb +2 -2
  102. data/lib/weft/generated/models/purchase_response.rb +2 -2
  103. data/lib/weft/generated/models/rate_limit_response.rb +2 -2
  104. data/lib/weft/generated/models/resend_confirmation_request.rb +2 -2
  105. data/lib/weft/generated/models/resource_enrollment_request.rb +2 -2
  106. data/lib/weft/generated/models/resource_enrollment_response.rb +2 -2
  107. data/lib/weft/generated/models/resource_stats.rb +2 -2
  108. data/lib/weft/generated/models/search_access_method.rb +2 -2
  109. data/lib/weft/generated/models/search_capability_ref.rb +2 -2
  110. data/lib/weft/generated/models/search_compact_access.rb +179 -0
  111. data/lib/weft/generated/models/search_compact_access_price.rb +176 -0
  112. data/lib/weft/generated/models/search_compact_next_call.rb +275 -0
  113. data/lib/weft/generated/models/search_compact_result.rb +389 -0
  114. data/lib/weft/generated/models/search_compact_result_attribution.rb +166 -0
  115. data/lib/weft/generated/models/search_compact_result_blocked_scopes_inner.rb +156 -0
  116. data/lib/weft/generated/models/search_compact_result_operation.rb +183 -0
  117. data/lib/weft/generated/models/search_compact_result_provider.rb +156 -0
  118. data/lib/weft/generated/models/search_curated_callability.rb +2 -2
  119. data/lib/weft/generated/models/search_curated_execution.rb +2 -2
  120. data/lib/weft/generated/models/search_curated_operation.rb +2 -2
  121. data/lib/weft/generated/models/search_curated_service.rb +2 -2
  122. data/lib/weft/generated/models/search_curated_source.rb +2 -2
  123. data/lib/weft/generated/models/search_endpoint_call.rb +4 -4
  124. data/lib/weft/generated/models/search_endpoint_call_input_schema.rb +103 -0
  125. data/lib/weft/generated/models/search_endpoint_hit.rb +25 -6
  126. data/lib/weft/generated/models/search_endpoint_price.rb +2 -2
  127. data/lib/weft/generated/models/search_error_response.rb +4 -4
  128. data/lib/weft/generated/models/search_execution_mode_filter.rb +2 -2
  129. data/lib/weft/generated/models/search_filter_spec.rb +2 -2
  130. data/lib/weft/generated/models/search_helper_docs.rb +175 -0
  131. data/lib/weft/generated/models/search_method_filter.rb +2 -2
  132. data/lib/weft/generated/models/search_payment_offer.rb +2 -2
  133. data/lib/weft/generated/models/search_price_atomic_filter.rb +2 -2
  134. data/lib/weft/generated/models/search_price_usd_filter.rb +2 -2
  135. data/lib/weft/generated/models/search_protocol_filter.rb +2 -2
  136. data/lib/weft/generated/models/search_provider_ref.rb +2 -2
  137. data/lib/weft/generated/models/search_request.rb +50 -6
  138. data/lib/weft/generated/models/search_resource_type_filter.rb +2 -2
  139. data/lib/weft/generated/models/search_response.rb +71 -8
  140. data/lib/weft/generated/models/search_response_warnings_inner.rb +2 -2
  141. data/lib/weft/generated/models/search_result.rb +2 -2
  142. data/lib/weft/generated/models/search_string_set_filter.rb +2 -2
  143. data/lib/weft/generated/models/search_weft_fetch_compatibility.rb +28 -6
  144. data/lib/weft/generated/models/sign_in_request.rb +2 -2
  145. data/lib/weft/generated/models/sign_up_request.rb +2 -2
  146. data/lib/weft/generated/models/spending_policy.rb +2 -2
  147. data/lib/weft/generated/models/user.rb +2 -2
  148. data/lib/weft/generated/models/user_principal.rb +2 -2
  149. data/lib/weft/generated/models/wallet.rb +2 -2
  150. data/lib/weft/generated/version.rb +3 -3
  151. data/lib/weft/sdk.rb +3 -1
  152. metadata +39 -4
@@ -1,27 +1,35 @@
1
+ require 'json'
2
+
3
+ require_relative 'client'
4
+
1
5
  module Weft
2
6
  module Facilitator
3
7
  module Fee
4
- DEFAULT_TTL = 300
8
+ DEFAULT_TTL_MS = 5 * 60 * 1000
5
9
 
6
10
  @cache = nil
7
11
 
8
12
  class << self
9
- def get_fee_info(client: Client.new, ttl: DEFAULT_TTL)
13
+ def get_fee_info(client: Client.new, ttl: DEFAULT_TTL_MS)
10
14
  return @cache[:fee] if cache_valid?
11
15
 
12
16
  supported = client.supported
13
- fee = supported['fee']
17
+ fee = supported['fee'] || supported[:fee]
14
18
  raise 'Fee information not found in /supported response' unless fee
15
19
 
16
20
  validate_fee!(fee)
21
+ normalized = {
22
+ 'amount' => fee['amount'] || fee[:amount],
23
+ 'asset' => fee['asset'] || fee[:asset],
24
+ 'network' => fee['network'] || fee[:network]
25
+ }
17
26
 
18
27
  @cache = {
19
- fee: fee,
20
- fetched_at: Time.now.to_i,
28
+ fee: normalized,
29
+ fetched_at: now_ms,
21
30
  ttl: ttl
22
31
  }
23
-
24
- fee
32
+ normalized
25
33
  end
26
34
 
27
35
  def invalidate_fee_cache
@@ -33,13 +41,20 @@ module Weft
33
41
  def cache_valid?
34
42
  return false unless @cache
35
43
 
36
- Time.now.to_i - @cache[:fetched_at] < @cache[:ttl]
44
+ now_ms - @cache[:fetched_at] < @cache[:ttl]
45
+ end
46
+
47
+ def now_ms
48
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
37
49
  end
38
50
 
39
51
  def validate_fee!(fee)
40
- raise 'Invalid fee structure: amount must be a string' unless fee['amount'].is_a?(String)
41
- raise 'Invalid fee structure: asset must be a string' unless fee['asset'].is_a?(String)
42
- raise 'Invalid fee structure: network must be a string' unless fee['network'].is_a?(String)
52
+ amount = fee['amount'] || fee[:amount]
53
+ asset = fee['asset'] || fee[:asset]
54
+ network = fee['network'] || fee[:network]
55
+ raise 'Invalid fee structure: amount must be a string' unless amount.is_a?(String)
56
+ raise 'Invalid fee structure: asset must be a string' unless asset.is_a?(String)
57
+ raise 'Invalid fee structure: network must be a string' unless network.is_a?(String)
43
58
  end
44
59
  end
45
60
  end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../generated/version'
4
+ require_relative 'json_wire'
5
+ require_relative 'product'
6
+
7
+ module Weft
8
+ module Facilitator
9
+ WEFT_DECLARED_HEADER = 'X-Weft-Declared'
10
+ WEFT_API_KEY_HEADER = 'X-API-Key'
11
+ HEADER_SAFE = /\A[\x21-\x7e]+\z/
12
+ ADAPTER_NAME = 'rack'
13
+
14
+ module Handshake
15
+ module_function
16
+
17
+ def build_auth_headers(adapter, api_key, declaration)
18
+ declaration = stringify_declaration(declaration)
19
+ key = resolve_api_key(api_key)
20
+ supported = {
21
+ 'User-Agent' => "weft-sdk-#{adapter}/#{sdk_version}"
22
+ }
23
+ supported['Authorization'] = "Bearer #{key}" unless key.nil?
24
+
25
+ declared = declared_identity_value(declaration)
26
+ supported[WEFT_DECLARED_HEADER] = declared unless declared.nil?
27
+
28
+ headers = { 'supported' => supported }
29
+ unless key.nil?
30
+ headers['settle'] = { WEFT_API_KEY_HEADER => key }
31
+ headers['verify'] = { WEFT_API_KEY_HEADER => key }
32
+ end
33
+ headers
34
+ end
35
+
36
+ def declared_identity_value(declaration)
37
+ declared = Product.sanitize_product_identity(declaration)
38
+ dimensions = Product.resolve_dimensions(declaration['dimensions'], Product.create_warn)
39
+ payload = {}
40
+ payload['name'] = declared['name'] if declared.key?('name')
41
+ payload['type'] = declared['type'] if declared.key?('type')
42
+ payload['tags'] = declared['tags'] if declared.key?('tags')
43
+ payload['icon_url'] = declared['iconUrl'] if declared.key?('iconUrl')
44
+ payload['dimensions'] = dimensions unless dimensions.nil?
45
+ return nil if payload.empty?
46
+
47
+ JsonWire.base64url_encode(JsonWire.generate(payload))
48
+ end
49
+
50
+ def resolve_api_key(api_key)
51
+ return nil if api_key.nil?
52
+ unless api_key.is_a?(String)
53
+ warn "[weft] ignoring apiKey: expected a string, got #{js_type(api_key)}"
54
+ return nil
55
+ end
56
+
57
+ trimmed = api_key.strip
58
+ if trimmed.empty?
59
+ warn '[weft] ignoring empty apiKey'
60
+ return nil
61
+ end
62
+ unless HEADER_SAFE.match?(trimmed)
63
+ warn '[weft] ignoring apiKey: it contains whitespace or non-printable ' \
64
+ 'characters that cannot travel in an HTTP header'
65
+ return nil
66
+ end
67
+
68
+ trimmed
69
+ end
70
+
71
+ def stringify_declaration(declaration)
72
+ return {} unless declaration.is_a?(Hash)
73
+
74
+ declaration.each_with_object({}) do |(key, value), out|
75
+ out[key.to_s] = value
76
+ end
77
+ end
78
+
79
+ def sdk_version
80
+ defined?(SDK) ? SDK::VERSION : VERSION
81
+ end
82
+
83
+ def js_type(value)
84
+ case value
85
+ when Integer, Float then 'number'
86
+ when TrueClass, FalseClass then 'boolean'
87
+ else 'object'
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'json'
5
+
6
+ module Weft
7
+ module Facilitator
8
+ # JSON.stringify and the two base64 forms @x402/core puts on the wire.
9
+ module JsonWire
10
+ module_function
11
+
12
+ def generate(value)
13
+ return nil if circular?(value)
14
+
15
+ JSON.generate(sanitize(value))
16
+ rescue JSON::GeneratorError, JSON::NestingError
17
+ nil
18
+ end
19
+
20
+ def circular?(value, seen = {})
21
+ case value
22
+ when Hash, Array
23
+ return true if seen[value.object_id]
24
+
25
+ seen[value.object_id] = true
26
+ children = value.is_a?(Hash) ? value.values : value
27
+ children.any? { |item| circular?(item, seen) }
28
+ else
29
+ false
30
+ end
31
+ end
32
+
33
+ def sanitize(value)
34
+ case value
35
+ when Hash
36
+ value.each_with_object({}) do |(key, item), out|
37
+ out[key.is_a?(Symbol) ? key.to_s : key] = sanitize(item)
38
+ end
39
+ when Array
40
+ value.map { |item| sanitize(item) }
41
+ when Float
42
+ value.finite? ? value : nil
43
+ else
44
+ value
45
+ end
46
+ end
47
+
48
+ def base64_encode(text)
49
+ Base64.strict_encode64(text.b)
50
+ end
51
+
52
+ def base64_decode(text)
53
+ Base64.strict_decode64(text).force_encoding(Encoding::UTF_8)
54
+ end
55
+
56
+ def base64url_encode(text)
57
+ Base64.urlsafe_encode64(text.b, padding: false)
58
+ end
59
+
60
+ def base64url_decode(text)
61
+ padded = text.tr('-_', '+/')
62
+ padded += '=' * ((4 - (padded.length % 4)) % 4)
63
+ Base64.decode64(padded).force_encoding(Encoding::UTF_8)
64
+ end
65
+
66
+ def encode_json(value)
67
+ json = generate(value)
68
+ raise ArgumentError, 'value is not JSON' if json.nil?
69
+
70
+ base64_encode(json)
71
+ end
72
+ end
73
+ end
74
+ end