aspera-cli 4.26.2 → 4.27.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 (136) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +281 -21
  4. data/CONTRIBUTING.md +98 -7
  5. data/bin/ascli +3 -3
  6. data/docs/README.md +10757 -0
  7. data/docs/test-mcp-with-ai.md +202 -0
  8. data/lib/aspera/agent/base.rb +20 -6
  9. data/lib/aspera/agent/connect.rb +53 -16
  10. data/lib/aspera/agent/desktop.rb +52 -17
  11. data/lib/aspera/agent/direct.rb +26 -24
  12. data/lib/aspera/agent/httpgw.rb +1 -1
  13. data/lib/aspera/agent/node.rb +32 -0
  14. data/lib/aspera/agent/transferd.rb +39 -3
  15. data/lib/aspera/api/alee.rb +1 -1
  16. data/lib/aspera/api/aoc.rb +14 -16
  17. data/lib/aspera/api/cos_node.rb +1 -1
  18. data/lib/aspera/api/faspex.rb +15 -14
  19. data/lib/aspera/api/httpgw.rb +4 -4
  20. data/lib/aspera/api/node.rb +26 -23
  21. data/lib/aspera/api/queries/bss_subscription_account.graphql +18 -0
  22. data/lib/aspera/api/queries/bss_subscription_usage.graphql +18 -0
  23. data/lib/aspera/ascmd.rb +21 -19
  24. data/lib/aspera/ascp/installation.rb +37 -38
  25. data/lib/aspera/ascp/management.rb +2 -3
  26. data/lib/aspera/cli/ascp_actions.rb +155 -0
  27. data/lib/aspera/cli/async_transfer_store.rb +81 -0
  28. data/lib/aspera/cli/bootstrapper.rb +197 -0
  29. data/lib/aspera/cli/command_registry.rb +145 -0
  30. data/lib/aspera/cli/command_spec.rb +138 -0
  31. data/lib/aspera/cli/context.rb +16 -3
  32. data/lib/aspera/cli/error.rb +16 -0
  33. data/lib/aspera/cli/extended_value.rb +2 -1
  34. data/lib/aspera/cli/formatter.rb +41 -27
  35. data/lib/aspera/cli/gem_checker.rb +65 -0
  36. data/lib/aspera/cli/hints.rb +2 -2
  37. data/lib/aspera/cli/http.rb +70 -19
  38. data/lib/aspera/cli/info.rb +2 -0
  39. data/lib/aspera/cli/mailer.rb +97 -0
  40. data/lib/aspera/cli/mcp_tool.rb +198 -0
  41. data/lib/aspera/cli/options.schema.yaml +364 -7
  42. data/lib/aspera/cli/{manager.rb → parser.rb} +334 -142
  43. data/lib/aspera/cli/plugins/alee.rb +20 -22
  44. data/lib/aspera/cli/plugins/aoc.rb +1166 -853
  45. data/lib/aspera/cli/plugins/ats.rb +200 -161
  46. data/lib/aspera/cli/plugins/base.rb +457 -100
  47. data/lib/aspera/cli/plugins/basic_auth.rb +14 -4
  48. data/lib/aspera/cli/plugins/config.rb +428 -754
  49. data/lib/aspera/cli/plugins/console.rb +106 -64
  50. data/lib/aspera/cli/plugins/cos.rb +44 -32
  51. data/lib/aspera/cli/plugins/factory.rb +4 -4
  52. data/lib/aspera/cli/plugins/faspex.rb +294 -257
  53. data/lib/aspera/cli/plugins/faspex5.rb +585 -357
  54. data/lib/aspera/cli/plugins/faspio.rb +49 -51
  55. data/lib/aspera/cli/plugins/httpgw.rb +18 -25
  56. data/lib/aspera/cli/plugins/mcp.rb +279 -0
  57. data/lib/aspera/cli/plugins/node.rb +998 -794
  58. data/lib/aspera/cli/plugins/oauth.rb +7 -10
  59. data/lib/aspera/cli/plugins/orchestrator.rb +111 -134
  60. data/lib/aspera/cli/plugins/preview.rb +180 -139
  61. data/lib/aspera/cli/plugins/server.rb +124 -79
  62. data/lib/aspera/cli/plugins/shares.rb +301 -107
  63. data/lib/aspera/cli/preset_actions.rb +139 -0
  64. data/lib/aspera/cli/preset_manager.rb +13 -12
  65. data/lib/aspera/cli/result.rb +61 -11
  66. data/lib/aspera/cli/runner.rb +275 -141
  67. data/lib/aspera/cli/secret_finder.rb +40 -0
  68. data/lib/aspera/cli/special_values.rb +1 -0
  69. data/lib/aspera/cli/sync_actions.rb +83 -59
  70. data/lib/aspera/cli/terminal_formatter.rb +1 -1
  71. data/lib/aspera/cli/transfer_actions.rb +83 -0
  72. data/lib/aspera/cli/transfer_agent.rb +116 -51
  73. data/lib/aspera/cli/transfer_progress.rb +3 -3
  74. data/lib/aspera/cli/vault_manager.rb +57 -0
  75. data/lib/aspera/cli/version.rb +1 -1
  76. data/lib/aspera/cli/wizard.rb +21 -20
  77. data/lib/aspera/command_line_builder.rb +22 -22
  78. data/lib/aspera/data_repository.rb +2 -1
  79. data/lib/aspera/dot_container.rb +4 -4
  80. data/lib/aspera/environment.rb +18 -11
  81. data/lib/aspera/exec_spec.rb +13 -0
  82. data/lib/aspera/faspex_gw.rb +1 -1
  83. data/lib/aspera/faspex_postproc.rb +4 -3
  84. data/lib/aspera/graphql.rb +35 -0
  85. data/lib/aspera/hash_ext.rb +6 -0
  86. data/lib/aspera/json_rpc/client.rb +62 -0
  87. data/lib/aspera/json_rpc/version.rb +7 -0
  88. data/lib/aspera/keychain/encrypted_hash.rb +1 -1
  89. data/lib/aspera/keychain/factory.rb +3 -3
  90. data/lib/aspera/keychain/macos_security.rb +3 -3
  91. data/lib/aspera/link_header.rb +82 -0
  92. data/lib/aspera/log.rb +17 -3
  93. data/lib/aspera/markdown.rb +85 -2
  94. data/lib/aspera/node_simulator.rb +3 -1
  95. data/lib/aspera/oauth/base.rb +5 -5
  96. data/lib/aspera/oauth/boot.rb +2 -2
  97. data/lib/aspera/oauth/factory.rb +7 -5
  98. data/lib/aspera/oauth/jwt.rb +4 -4
  99. data/lib/aspera/oauth/web.rb +2 -2
  100. data/lib/aspera/persistency_action_once.rb +8 -8
  101. data/lib/aspera/persistency_folder.rb +10 -3
  102. data/lib/aspera/preview/file_types.rb +3 -3
  103. data/lib/aspera/preview/generator.rb +3 -3
  104. data/lib/aspera/preview/utils.rb +1 -1
  105. data/lib/aspera/products/connect.rb +1 -1
  106. data/lib/aspera/products/other.rb +1 -1
  107. data/lib/aspera/products/transferd.rb +1 -1
  108. data/lib/aspera/proxy_auto_config.rb +7 -6
  109. data/lib/aspera/rest.rb +10 -36
  110. data/lib/aspera/rest_error_analyzer.rb +3 -3
  111. data/lib/aspera/rest_errors_aspera.rb +0 -10
  112. data/lib/aspera/rest_list.rb +3 -3
  113. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1853 -510
  114. data/lib/aspera/schema/documentation.rb +57 -26
  115. data/lib/aspera/schema/reader.rb +59 -10
  116. data/lib/aspera/schema/registry.rb +39 -6
  117. data/lib/aspera/secret_hider.rb +7 -2
  118. data/lib/aspera/ssl.rb +4 -4
  119. data/lib/aspera/sync/conf.schema.yaml +2 -2
  120. data/lib/aspera/sync/operations.rb +10 -13
  121. data/lib/aspera/temp_file_manager.rb +1 -1
  122. data/lib/aspera/timer_limiter.rb +1 -1
  123. data/lib/aspera/transfer/faux_file.rb +24 -11
  124. data/lib/aspera/transfer/parameters.rb +23 -21
  125. data/lib/aspera/transfer/result.rb +74 -0
  126. data/lib/aspera/transfer/resumer.rb +6 -6
  127. data/lib/aspera/transfer/spec.rb +18 -1
  128. data/lib/aspera/transfer/spec.schema.yaml +12 -3
  129. data/lib/aspera/uri_reader.rb +2 -2
  130. data/lib/aspera/web_auth.rb +2 -2
  131. data/lib/aspera/web_server_simple.rb +8 -5
  132. data/lib/aspera/yaml.rb +2 -1
  133. data.tar.gz.sig +0 -0
  134. metadata +25 -3
  135. metadata.gz.sig +0 -0
  136. data/lib/aspera/json_rpc.rb +0 -52
data/lib/aspera/rest.rb CHANGED
@@ -77,6 +77,9 @@ module Aspera
77
77
  # and error are analyzed in RestErrorAnalyzer
78
78
  class Rest
79
79
  class << self
80
+ # Build a Basic authentication header value
81
+ # @param user [String] Username
82
+ # @param pass [String] Password
80
83
  # @return [String] Basic auth token
81
84
  def basic_authorization(user, pass); return "Basic #{Base64.strict_encode64("#{user}:#{pass}")}"; end
82
85
 
@@ -93,7 +96,8 @@ module Aspera
93
96
  # Check if php style is specified.
94
97
  # `nil` values in query result in key without value, e.g. `?a`, while empty string values result in `?a=`.
95
98
  # @param url [String] The URL without query.
96
- # @param query [Hash,Array,String] The query.
99
+ # @param query [Hash,Array,String] The query parameters.
100
+ # @return [URI] The built URI.
97
101
  def build_uri(url, query)
98
102
  uri = URI.parse(url)
99
103
  Aspera.assert_values(uri.scheme, %w[http https]){'URI scheme'}
@@ -118,6 +122,7 @@ module Aspera
118
122
  # Either p=1&p=2 (default)
119
123
  # or p[]=1&p[]=2 (if `:x_array_php_style` is set to true in query)
120
124
  # @param query [Hash] HTTP query as hash
125
+ # @return [Array<Array>] Array of [key, value] pairs suitable for URI.encode_www_form
121
126
  def h_to_query_array(query)
122
127
  Aspera.assert_type(query, Hash)
123
128
  suffix = query.delete(:x_array_php_style) ? '[]' : nil
@@ -155,37 +160,6 @@ module Aspera
155
160
  end
156
161
  end
157
162
 
158
- # Parse Link header according to RFC 8288 to extract a specific relation
159
- # @param link_header [String, nil] The Link header value
160
- # @param rel [String] The relation to look for (default: 'next')
161
- # @return [String, nil] The URL of the link with the specified relation, or nil
162
- def parse_link_header(link_header, rel: 'next')
163
- return if link_header.nil? || link_header.empty?
164
- # RFC 8288: Link header format is: <URI>; param1=value1; param2=value2, <URI2>; ...
165
- # We look for the link with the specified rel
166
- link_header.split(',').each do |link_part|
167
- link_part = link_part.strip
168
- # Extract URL between < and >
169
- url_match = link_part.match(/<([^>]+)>/)
170
- next unless url_match
171
- url = url_match[1]
172
- # Extract parameters after the URL
173
- params_str = link_part[url_match.end(0)..]
174
- # Check if this link has the specified rel (with or without quotes, case insensitive)
175
- next unless /;\s*rel\s*=\s*"?#{Regexp.escape(rel)}"?/i.match?(params_str)
176
- return url
177
- end
178
- # Fallback: if no rel found and looking for 'next', try the first link (backward compatibility)
179
- if rel.eql?('next')
180
- first_link = link_header.split(',').first&.strip
181
- if first_link
182
- url_match = first_link.match(/<([^>]+)>/)
183
- return url_match[1] if url_match
184
- end
185
- end
186
- nil
187
- end
188
-
189
163
  # Start a HTTP/S session, also used for web sockets
190
164
  # @param base_url [String] Base url of HTTP/S session
191
165
  # @return [Net::HTTP] A started HTTP session
@@ -279,7 +253,7 @@ module Aspera
279
253
  # Base common headers of API
280
254
  attr_reader :headers
281
255
 
282
- # @return creation parameters
256
+ # @return [Hash] creation parameters
283
257
  def params
284
258
  return {
285
259
  base_url: @base_url, # String
@@ -334,7 +308,7 @@ module Aspera
334
308
  @oauth = nil
335
309
  end
336
310
 
337
- # @return the OAuth object (create, or cached if already created)
311
+ # @return [OAuth::Base] the OAuth object (create, or cached if already created)
338
312
  def oauth
339
313
  if @oauth.nil?
340
314
  Aspera.assert(@auth_params[:type].eql?(:oauth2), 'no OAuth defined')
@@ -354,7 +328,7 @@ module Aspera
354
328
  # @param headers [Hash{String => String}] Additional headers (override Content-Type)
355
329
  # @param save_to_file [String, nil] File path to save response body
356
330
  # @param exception [Boolean] Whether to raise an exception on HTTP error
357
- # @param ret [Symbol] One of :data, :resp, :both controls return value
331
+ # @param ret [Symbol] One of :data, :resp, :both - controls return value
358
332
  # @return [Array(Hash, Net::HTTPResponse)] When `ret` is :both
359
333
  # @return [Net::HTTPResponse] When `ret` is :resp
360
334
  # @return [Hash] When `ret` is :data
@@ -485,7 +459,7 @@ module Aspera
485
459
  end
486
460
  end
487
461
  Log.log.debug{"result: code=#{result_http.code} mime=#{result_mime}"}
488
- # sometimes there is a UTF8 char (e.g. © )
462
+ # sometimes there is a UTF8 char (e.g. (c) )
489
463
  # TODO : related to mime type encoding ?
490
464
  # result_http.body.force_encoding('UTF-8') if result_http.body.is_a?(String)
491
465
  # Log.log.debug{"result: body=#{result_http.body}"}
@@ -32,9 +32,9 @@ module Aspera
32
32
  # Use this method to analyze a EST result and raise an exception
33
33
  # Analyzes REST call response and raises a RestCallError exception
34
34
  # if HTTP result code is not 2XX
35
- # @param req [Net::HTTPRequest]
36
- # @param data [Object]
37
- # @param http [Net::HTTPResponse]
35
+ # @param req [Net::HTTPRequest] the HTTP request
36
+ # @param data [Object] the parsed response body
37
+ # @param http [Net::HTTPResponse] the raw HTTP response
38
38
  def raise_on_error(req, data, http)
39
39
  Log.log.debug{"raise_on_error #{req.method} #{req.path} #{http.code}"}
40
40
  context = {
@@ -41,16 +41,6 @@ module Aspera
41
41
  end
42
42
  RestErrorAnalyzer.instance.add_simple_handler(name: 'T9:IBM cloud IAM', path: ['errorMessage'])
43
43
  RestErrorAnalyzer.instance.add_simple_handler(name: 'T10:faspex v4', path: ['user_message'])
44
- RestErrorAnalyzer.instance.add_handler('bss graphql') do |type, context|
45
- next unless context[:data].is_a?(Hash)
46
- d_t_s = context[:data]['errors']
47
- next unless d_t_s.is_a?(Array)
48
- d_t_s.each do |res|
49
- r_err = res['message']
50
- next unless r_err.is_a?(String)
51
- RestErrorAnalyzer.add_error(context, type, r_err)
52
- end
53
- end
54
44
  RestErrorAnalyzer.instance.add_handler('Orchestrator') do |type, context|
55
45
  next if context[:response].code.start_with?('2')
56
46
  data = context[:data]
@@ -15,7 +15,7 @@ module Aspera
15
15
  # @param value [String] Value of field of searched entity
16
16
  # @param field [String] Field of searched entity
17
17
  # @param query [Hash] Optional additional search query parameters
18
- # @returns [Hash] A single entity matching the search, or an exception if not found or multiple found
18
+ # @return [Hash] A single entity matching the search, or an exception if not found or multiple found
19
19
  def lookup_with_q(entity, value:, field: 'name', query: {})
20
20
  Aspera.assert_type(query, Hash){'query'}
21
21
  Aspera.assert_type(field, String){'field'}
@@ -35,8 +35,8 @@ module Aspera
35
35
  value_matches = matching_items.select{ |i| i[field].casecmp?(value)}
36
36
  case value_matches.length
37
37
  when 1 then return value_matches.first
38
- when 0 then raise %Q(#{entity}: Multiple case insensitive partial match for: "#{value}" in #{matching_items.map{ |i| i[field]}.join(', ')} but no case insensitive full match. Please be more specific or give exact #{field}.)
39
- else raise "Two entities cannot have the same case insensitive #{field}: #{value_matches.map{ |i| i[field]}}"
38
+ when 0 then raise Error, "#{entity}: Multiple case insensitive partial match for: \"#{value}\" in #{matching_items.map{ |i| i[field]}.join(', ')} but no case insensitive full match. Please be more specific or give exact #{field}."
39
+ else raise Error, "Two entities cannot have the same case insensitive #{field}: #{value_matches.map{ |i| i[field]}}"
40
40
  end
41
41
  end
42
42
  end