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
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'aspera/log'
4
4
  require 'aspera/assert'
5
+ require 'aspera/exec_spec'
5
6
  require 'aspera/command_line_builder'
6
7
  require 'aspera/temp_file_manager'
7
8
  require 'aspera/transfer/error'
@@ -38,8 +39,9 @@ module Aspera
38
39
  @file_list_folder ||= TempFileManager.instance.new_file_path_global('asession_filelists')
39
40
  end
40
41
 
41
- # File list is provided directly with `ascp` arguments
42
- # @columns ascp_args [Array,NilClass] `ascp` arguments
42
+ # Check whether a file list option is already present in the `ascp` argument list
43
+ # @param ascp_args [Array, nil] `ascp` arguments
44
+ # @return [Boolean] true if a file list option is present
43
45
  def ascp_args_file_list?(ascp_args)
44
46
  ascp_args&.any?{ |i| FILE_LIST_OPTIONS.include?(i)}
45
47
  end
@@ -47,7 +49,9 @@ module Aspera
47
49
 
48
50
  # @param job_spec [Hash] Transfer specification
49
51
  # @param ascp_args [Array] Other `ascp` args
52
+ # @param wss [Boolean] `true`: if both SSH and wss in ts: prefer wss
50
53
  # @param quiet [Boolean] Remove `ascp` progress bar if `true`
54
+ # @param file_list [Boolean] `true`: provide source list via a temp file, `false`: place directly on command line
51
55
  # @param client_ssh_key [:rsa,:dsa] Type of Aspera Client SSH key to use.
52
56
  # @param trusted_certs [Array<String>] List of path to trusted certificates stores when using WSS
53
57
  # @param check_ignore_cb [Proc] Callback to check if WSS connection shall ignore certificate validity
@@ -56,6 +60,7 @@ module Aspera
56
60
  ascp_args: nil,
57
61
  wss: true,
58
62
  quiet: true,
63
+ file_list: true,
59
64
  trusted_certs: nil,
60
65
  client_ssh_key: nil,
61
66
  check_ignore_cb: nil
@@ -66,6 +71,7 @@ module Aspera
66
71
  Aspera.assert_array_all(@ascp_args, String){'ascp_args'}
67
72
  @wss = wss
68
73
  @quiet = quiet
74
+ @file_list = file_list
69
75
  @trusted_certs = trusted_certs.nil? ? [] : trusted_certs
70
76
  Aspera.assert_type(@trusted_certs, Array){'trusted_certs'}
71
77
  @client_ssh_key = client_ssh_key.nil? ? :rsa : client_ssh_key.to_sym
@@ -74,7 +80,7 @@ module Aspera
74
80
  @builder = CommandLineBuilder.new(@job_spec, Spec::SCHEMA, CommandLineConverter)
75
81
  end
76
82
 
77
- # either place source files on command line, or add file list file
83
+ # Place source files on command line or in a temp file list, controlled by @file_list and file_list_folder
78
84
  def process_file_list
79
85
  # is the file list provided through ascp parameters?
80
86
  ascp_file_list_provided = self.class.ascp_args_file_list?(@ascp_args)
@@ -88,8 +94,8 @@ module Aspera
88
94
  Aspera.assert(!ascp_file_list_provided, 'file list provided both in transfer spec and ascp file list. Remove one of them.')
89
95
  Aspera.assert(ts_paths_array.all?{ |i| i.key?('source')}, "All elements of paths must have a 'source' key")
90
96
  is_pair_list = ts_paths_array.any?{ |i| i.key?('destination')}
91
- raise "All elements of paths must be consistent with 'destination' key" if is_pair_list && !ts_paths_array.all?{ |i| i.key?('destination')}
92
- if self.class.file_list_folder.nil?
97
+ Aspera.assert(!(is_pair_list && !ts_paths_array.all?{ |i| i.key?('destination')})){"All elements of paths must be consistent with 'destination' key"}
98
+ if !@file_list || self.class.file_list_folder.nil?
93
99
  Aspera.assert(!is_pair_list, 'file pair list is not supported when file list folder is not set')
94
100
  # not safe for special characters ? (maybe not, depends on OS)
95
101
  Log.log.debug('placing source file list on command line (no file list file)')
@@ -112,7 +118,7 @@ module Aspera
112
118
  @builder.add_command_line_options("#{file_list_option}=#{file_list_file}") unless file_list_option.nil?
113
119
  end
114
120
 
115
- # @return the list of certificates (option `-i`) to use when token/ssh or wss are used
121
+ # @return [Array<String>] the list of certificates (option `-i`) to use when token/ssh or wss are used
116
122
  def remote_certificates
117
123
  certificates_to_use = []
118
124
  # use web socket secure for session ?
@@ -150,11 +156,7 @@ module Aspera
150
156
 
151
157
  # Translate transfer spec to env vars and command line arguments for `ascp`
152
158
  def ascp_args
153
- env_args = {
154
- args: [],
155
- env: {},
156
- name: :ascp
157
- }
159
+ exec_spec = Aspera::ExecSpec.new(exec: :ascp)
158
160
 
159
161
  # Special cases
160
162
  @job_spec.delete('source_root') if @job_spec.key?('source_root') && @job_spec['source_root'].empty?
@@ -165,7 +167,7 @@ module Aspera
165
167
  # Add ssh or wss certificates
166
168
  # (reverse, to keep order, as we unshift)
167
169
  remote_certificates&.reverse_each do |cert|
168
- env_args[:args].unshift('-i', cert)
170
+ exec_spec.args.unshift('-i', cert)
169
171
  end
170
172
 
171
173
  case (delete_source = @builder.read_param('delete_source'))
@@ -183,9 +185,9 @@ module Aspera
183
185
  base64_destination = false
184
186
  # symbol must be index of Ascp::Installation.paths
185
187
  if @builder.read_param('use_ascp4')
186
- env_args[:name] = :ascp4
188
+ exec_spec.exec = :ascp4
187
189
  else
188
- env_args[:name] = :ascp
190
+ exec_spec.exec = :ascp
189
191
  base64_destination = true
190
192
  end
191
193
  # destination will be base64 encoded, put this before source path arguments
@@ -200,17 +202,17 @@ module Aspera
200
202
  destination_folder = Base64.strict_encode64(destination_folder) if base64_destination
201
203
  # destination MUST be last command line argument to ascp
202
204
  @builder.add_command_line_options(destination_folder)
203
- @builder.add_env_args(env_args)
204
- env_args[:args].unshift('-q') if @quiet
205
+ @builder.add_to_exec_spec(exec_spec)
206
+ exec_spec.args.unshift('-q') if @quiet
205
207
  # add fallback cert and key as arguments if needed
206
208
  if HTTP_FALLBACK_ACTIVATION_VALUES.include?(@job_spec['http_fallback'])
207
- env_args[:args].unshift('-Y', Ascp::Installation.instance.path(:fallback_private_key))
208
- env_args[:args].unshift('-I', Ascp::Installation.instance.path(:fallback_certificate))
209
+ exec_spec.args.unshift('-Y', Ascp::Installation.instance.path(:fallback_private_key))
210
+ exec_spec.args.unshift('-I', Ascp::Installation.instance.path(:fallback_certificate))
209
211
  end
210
212
  # disable redis in client, only for ascp, this makes ascp4 fail
211
- env_args[:env]['ASPERA_TEST_REDIS_DISABLE'] = 'true' if env_args[:name].eql?(:ascp)
212
- Log.log.debug{"ascp args: #{env_args}"}
213
- return env_args
213
+ exec_spec.env['ASPERA_TEST_REDIS_DISABLE'] = 'true' if exec_spec.exec.eql?(:ascp)
214
+ Log.log.debug{"ascp args: #{exec_spec}"}
215
+ return exec_spec
214
216
  end
215
217
  DELETE_EQUIV = %w[remove_after_transfer remove_empty_directories remove_empty_source_directory]
216
218
  # `ascp` options to provide a file list
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspera
4
+ module Transfer
5
+ # Typed result of a transfer operation.
6
+ # Replaces the ad-hoc Array polymorphism previously returned by agents.
7
+ #
8
+ # Agents return one of the three concrete sub-classes:
9
+ # Transfer::Result::Success — all sessions completed successfully
10
+ # Transfer::Result::Error — at least one session failed
11
+ # Transfer::Result::Async — transfer submitted to a daemon; not yet complete
12
+ class Result
13
+ # All sessions completed successfully.
14
+ class Success < Result
15
+ def to_s
16
+ 'success'
17
+ end
18
+ end
19
+
20
+ # At least one session failed.
21
+ class Error < Result
22
+ # @return [StandardError]
23
+ attr_reader :exception
24
+
25
+ def initialize(exception)
26
+ super()
27
+ @exception = exception
28
+ end
29
+
30
+ def to_s
31
+ "error: #{@exception.message}"
32
+ end
33
+ end
34
+
35
+ # Transfer submitted to an external daemon; status not yet known.
36
+ class Async < Result
37
+ # @return [String] UUID assigned to this background job
38
+ attr_reader :job_id
39
+ # @return [String] initial status string (e.g. 'running')
40
+ attr_reader :status
41
+
42
+ def initialize(job_id:, status: 'running')
43
+ super()
44
+ @job_id = job_id
45
+ @status = status
46
+ end
47
+
48
+ # Convenience: export as a plain Hash for serialization / display
49
+ def to_h
50
+ {'job_id' => @job_id, 'status' => @status}
51
+ end
52
+
53
+ def to_s
54
+ "async job_id=#{@job_id} status=#{@status}"
55
+ end
56
+ end
57
+
58
+ # Factory helpers
59
+ class << self
60
+ def success
61
+ Success.new
62
+ end
63
+
64
+ def error(exception)
65
+ Error.new(exception)
66
+ end
67
+
68
+ def async(job_id:, status: 'running')
69
+ Async.new(job_id: job_id, status: status)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -12,20 +12,20 @@ module Aspera
12
12
  # @param iter_max [Integer] Maximum number of executions
13
13
  # @param sleep_initial [Integer] Initial wait to re-execute
14
14
  # @param sleep_factor [Integer] Multiplier
15
- # @param sleep_max. [Integer] Max iterations
15
+ # @param sleep_max [Integer] Max iterations
16
16
  def initialize(
17
17
  iter_max: 7,
18
18
  sleep_initial: 2,
19
19
  sleep_factor: 2,
20
20
  sleep_max: 60
21
21
  )
22
- Aspera.assert_type(iter_max, Integer){k}
22
+ Aspera.assert_type(iter_max, Integer){'iter_max'}
23
23
  @iter_max = iter_max
24
- Aspera.assert_type(sleep_initial, Integer){k}
24
+ Aspera.assert_type(sleep_initial, Integer){'sleep_initial'}
25
25
  @sleep_initial = sleep_initial
26
- Aspera.assert_type(sleep_factor, Integer){k}
26
+ Aspera.assert_type(sleep_factor, Integer){'sleep_factor'}
27
27
  @sleep_factor = sleep_factor
28
- Aspera.assert_type(sleep_max, Integer){k}
28
+ Aspera.assert_type(sleep_max, Integer){'sleep_max'}
29
29
  @sleep_max = sleep_max
30
30
  end
31
31
 
@@ -53,7 +53,7 @@ module Aspera
53
53
  # do not retry non-retryable
54
54
  raise unless e.retryable?
55
55
  # exit if we exceed the max number of retry
56
- raise Error, "Maximum number of retry reached: #{@iter_max}" if remaining_resumes <= 0
56
+ Aspera.assert(remaining_resumes > 0, type: Error){"Maximum number of retry reached: #{@iter_max}"}
57
57
  end
58
58
 
59
59
  # take this retry in account
@@ -34,7 +34,13 @@ module Aspera
34
34
  'sparse_csum' => 'sparse_checksum',
35
35
  'full_csum' => 'full_checksum'
36
36
  }
37
- private_constant :POLICY_FIX
37
+ # Multipliers for target_rate suffix (result is in kbps)
38
+ RATE_SUFFIX_KBPS = {
39
+ 'k' => 1,
40
+ 'm' => 1_000,
41
+ 'g' => 1_000_000
42
+ }.freeze
43
+ private_constant :POLICY_FIX, :RATE_SUFFIX_KBPS
38
44
  # translate upload/download to send/receive
39
45
  def transfer_type_to_direction(transfer_type)
40
46
  XFER_TYPE_TO_DIR.fetch(transfer_type)
@@ -45,6 +51,17 @@ module Aspera
45
51
  XFER_DIR_TO_TYPE.fetch(direction)
46
52
  end
47
53
 
54
+ # Parse a human-readable rate string (as accepted by ascp -l) into an integer kbps value.
55
+ # Accepted: plain integer (kbps), or integer + suffix k/K (kbps), m/M (x1000 kbps), g/G (x1000000 kbps).
56
+ # @param value [String] e.g. "100m", "500000", "1g"
57
+ # @return [Integer] value in kbps
58
+ def rate_string_to_kbps(value)
59
+ m = value.to_s.strip.match(/\A(\d+)([kKmMgG])?\z/)
60
+ Aspera.assert(m){"Invalid rate value: #{value.inspect}. Expected integer with optional suffix k/K, m/M or g/G."}
61
+ multiplier = m[2] ? RATE_SUFFIX_KBPS.fetch(m[2].downcase) : 1
62
+ return m[1].to_i * multiplier
63
+ end
64
+
48
65
  def fix_transferd_resume_policy(transfer_spec)
49
66
  # Fix discrepancy in transfer spec
50
67
  transfer_spec['resume_policy'] = POLICY_FIX[transfer_spec['resume_policy']] if transfer_spec.key?('resume_policy')
@@ -377,6 +377,7 @@ properties:
377
377
  description: Specifies whether to precalculate the job size.
378
378
  type: boolean
379
379
  x-cli-switch: true
380
+ x-cli-false: --precalculate-disable
380
381
  preserve_access_time:
381
382
  description: >-
382
383
  Preserve the source-file access timestamps at the destination.
@@ -711,11 +712,11 @@ properties:
711
712
  Refer to HSTS manual for more details.
712
713
 
713
714
  - `follow`: Follow a symbolic link and transfer the contents of the linked file or directory when the link target is in the user's docroot.
714
-
715
+
715
716
  - `copy`: Copy only the symbolic link. If a file with the same name exists at the destination, the symbolic link does not replace the file.
716
-
717
+
717
718
  - `skip`: Skip-symbolic links. The link or the file to which it points are not transferred.
718
-
719
+
719
720
  - `copy+force`: Copy only the symbolic link. If a file with the same name exists at the destination, the symbolic link replaces the file. If the file of the same name at the destination is a symbolic link to a directory, it is not replaced.
720
721
  type: string
721
722
  enum:
@@ -753,6 +754,14 @@ properties:
753
754
  description: Specifies desired speed for the transfer.
754
755
  type: integer
755
756
  x-cli-option: -l
757
+ target_rate:
758
+ description: >-
759
+ Pseudo-parameter: desired transfer rate as a human-readable string with an optional unit suffix.
760
+ Converted to `target_rate_kbps` before the transfer is started, overriding it if both are present.
761
+ Accepted formats: plain integer (kbps), or integer followed by `k`/`K` (kbps), `m`/`M` (x1000 kbps), `g`/`G` (x1000000 kbps).
762
+ Examples: `100000`, `100000k`, `100m`, `1g`.
763
+ type: string
764
+ x-cli-special: true
756
765
  title:
757
766
  description: Title of the transfer.
758
767
  type: string
@@ -45,14 +45,14 @@ module Aspera
45
45
  end
46
46
  when SCHEME_FILE, NilClass
47
47
  local_file_path = uri.path
48
- raise Error, 'URL shall have a path, check syntax' if local_file_path.nil?
48
+ Aspera.assert(!local_file_path.nil?, type: Error){'URL shall have a path, check syntax'}
49
49
  local_file_path = File.expand_path(local_file_path.gsub(%r{^/}, '')) if %r{^/(~|.|..)/}.match?(local_file_path)
50
50
  return File.read(local_file_path)
51
51
  else Aspera.error_unexpected_value(uri.scheme){"scheme for [#{uri_to_read}]"}
52
52
  end
53
53
  end
54
54
 
55
- # @return Path to file with content at URL
55
+ # @return [String] path to file with content at URL
56
56
  def read_as_file(url)
57
57
  if url.start_with?(SCHEME_FILE_PFX1)
58
58
  # for file scheme, return directly the path
@@ -6,8 +6,8 @@ require 'aspera/assert'
6
6
  module Aspera
7
7
  # servlet called on callback: it records the callback request
8
8
  class WebAuthServlet < WEBrick::HTTPServlet::AbstractServlet
9
- # @param server [WEBrick::HTTPServer]
10
- # @param web_auth [WebAuth]
9
+ # @param server [WEBrick::HTTPServer] the HTTP server instance
10
+ # @param web_auth [WebAuth] the WebAuth instance to record the callback
11
11
  def initialize(server, web_auth)
12
12
  Log.log.debug('WebAuthServlet initialize')
13
13
  super(server)
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'webrick'
4
- require 'webrick/https'
5
4
  require 'aspera/log'
6
5
  require 'aspera/assert'
7
6
  require 'aspera/hash_ext'
8
- require 'openssl'
9
7
 
10
8
  module Aspera
11
9
  # Simple WEBrick server with HTTPS support
@@ -42,7 +40,7 @@ module Aspera
42
40
  cert
43
41
  end
44
42
 
45
- # @return a list of Certificates from chain file
43
+ # @return [Array<OpenSSL::X509::Certificate>] list of Certificates from chain file
46
44
  def read_chain_file(chain)
47
45
  File.read(chain).scan(/-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m).map{ |i| OpenSSL::X509::Certificate.new(i)}
48
46
  end
@@ -66,13 +64,18 @@ module Aspera
66
64
  when 'http'
67
65
  Log.log.debug('HTTP mode')
68
66
  when 'https'
67
+ # Required lazily: webrick/ssl.rb reads OpenSSL::OPENSSL_VERSION at load time,
68
+ # a constant CosmoRuby's OpenSSL shim does not define, so requiring it
69
+ # unconditionally at file load would break any command in a Cosmo build
70
+ # even when no HTTPS server is ever started.
71
+ require 'webrick/https'
69
72
  webrick_options[:SSLEnable] = true
70
73
  if cert.nil? && key.nil?
71
74
  webrick_options[:SSLCertName] = [['CN', WEBrick::Utils.getservername]]
72
75
  elsif cert && PKCS12_EXT.include?(File.extname(cert).downcase)
73
76
  # PKCS12
74
77
  Log.log.debug('Using PKCS12 certificate')
75
- raise Error, 'PKCS12 requires a key (password)' if key.nil?
78
+ Aspera.assert(!key.nil?, type: Error){'PKCS12 requires a key (password)'}
76
79
  pkcs12 = OpenSSL::PKCS12.new(File.read(cert), key)
77
80
  webrick_options[:SSLCertificate] = pkcs12.certificate
78
81
  webrick_options[:SSLPrivateKey] = pkcs12.key
@@ -90,7 +93,7 @@ module Aspera
90
93
  OpenSSL::X509::Certificate.new(File.read(cert))
91
94
  end
92
95
  webrick_options[:SSLExtraChainCert] = read_chain_file(chain) unless chain.nil?
93
- raise Error, 'key and cert do not match' unless webrick_options[:SSLCertificate].public_key.to_der == webrick_options[:SSLPrivateKey].public_key.to_der
96
+ Aspera.assert(webrick_options[:SSLCertificate].public_key.to_der == webrick_options[:SSLPrivateKey].public_key.to_der, type: Error){'key and cert do not match'}
94
97
  end
95
98
  end
96
99
  # call constructor of parent class, but capture STDERR
data/lib/aspera/yaml.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'aspera/assert'
3
4
  require 'yaml'
4
5
 
5
6
  module Aspera
@@ -42,7 +43,7 @@ module Aspera
42
43
  # @raise [RuntimeError] If duplicate keys are found
43
44
  def safe_load(yaml)
44
45
  duplicate_keys = find_duplicate_keys(Psych.parse_stream(yaml))
45
- raise "Duplicate keys: #{duplicate_keys.join('; ')}" unless duplicate_keys.empty?
46
+ Aspera.assert(duplicate_keys.empty?){"Duplicate keys: #{duplicate_keys.join('; ')}"}
46
47
  YAML.safe_load(yaml, permitted_classes: [Time, Date, Symbol])
47
48
  end
48
49
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspera-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.26.2
4
+ version: 4.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Martin
@@ -319,6 +319,8 @@ files:
319
319
  - README.md
320
320
  - bin/ascli
321
321
  - bin/asession
322
+ - docs/README.md
323
+ - docs/test-mcp-with-ai.md
322
324
  - lib/aspera/agent/base.rb
323
325
  - lib/aspera/agent/connect.rb
324
326
  - lib/aspera/agent/desktop.rb
@@ -334,19 +336,29 @@ files:
334
336
  - lib/aspera/api/faspex.rb
335
337
  - lib/aspera/api/httpgw.rb
336
338
  - lib/aspera/api/node.rb
339
+ - lib/aspera/api/queries/bss_subscription_account.graphql
340
+ - lib/aspera/api/queries/bss_subscription_usage.graphql
337
341
  - lib/aspera/ascmd.rb
338
342
  - lib/aspera/ascp/installation.rb
339
343
  - lib/aspera/ascp/management.rb
340
344
  - lib/aspera/assert.rb
345
+ - lib/aspera/cli/ascp_actions.rb
346
+ - lib/aspera/cli/async_transfer_store.rb
347
+ - lib/aspera/cli/bootstrapper.rb
348
+ - lib/aspera/cli/command_registry.rb
349
+ - lib/aspera/cli/command_spec.rb
341
350
  - lib/aspera/cli/context.rb
342
351
  - lib/aspera/cli/error.rb
343
352
  - lib/aspera/cli/extended_value.rb
344
353
  - lib/aspera/cli/formatter.rb
354
+ - lib/aspera/cli/gem_checker.rb
345
355
  - lib/aspera/cli/hints.rb
346
356
  - lib/aspera/cli/http.rb
347
357
  - lib/aspera/cli/info.rb
348
- - lib/aspera/cli/manager.rb
358
+ - lib/aspera/cli/mailer.rb
359
+ - lib/aspera/cli/mcp_tool.rb
349
360
  - lib/aspera/cli/options.schema.yaml
361
+ - lib/aspera/cli/parser.rb
350
362
  - lib/aspera/cli/plugins/alee.rb
351
363
  - lib/aspera/cli/plugins/aoc.rb
352
364
  - lib/aspera/cli/plugins/ats.rb
@@ -360,20 +372,25 @@ files:
360
372
  - lib/aspera/cli/plugins/faspex5.rb
361
373
  - lib/aspera/cli/plugins/faspio.rb
362
374
  - lib/aspera/cli/plugins/httpgw.rb
375
+ - lib/aspera/cli/plugins/mcp.rb
363
376
  - lib/aspera/cli/plugins/node.rb
364
377
  - lib/aspera/cli/plugins/oauth.rb
365
378
  - lib/aspera/cli/plugins/orchestrator.rb
366
379
  - lib/aspera/cli/plugins/preview.rb
367
380
  - lib/aspera/cli/plugins/server.rb
368
381
  - lib/aspera/cli/plugins/shares.rb
382
+ - lib/aspera/cli/preset_actions.rb
369
383
  - lib/aspera/cli/preset_manager.rb
370
384
  - lib/aspera/cli/result.rb
371
385
  - lib/aspera/cli/runner.rb
386
+ - lib/aspera/cli/secret_finder.rb
372
387
  - lib/aspera/cli/special_values.rb
373
388
  - lib/aspera/cli/sync_actions.rb
374
389
  - lib/aspera/cli/terminal_formatter.rb
390
+ - lib/aspera/cli/transfer_actions.rb
375
391
  - lib/aspera/cli/transfer_agent.rb
376
392
  - lib/aspera/cli/transfer_progress.rb
393
+ - lib/aspera/cli/vault_manager.rb
377
394
  - lib/aspera/cli/version.rb
378
395
  - lib/aspera/cli/wizard.rb
379
396
  - lib/aspera/colors.rb
@@ -389,18 +406,22 @@ files:
389
406
  - lib/aspera/data_repository.rb
390
407
  - lib/aspera/dot_container.rb
391
408
  - lib/aspera/environment.rb
409
+ - lib/aspera/exec_spec.rb
392
410
  - lib/aspera/faspex_gw.rb
393
411
  - lib/aspera/faspex_postproc.rb
394
412
  - lib/aspera/formatter_interface.rb
413
+ - lib/aspera/graphql.rb
395
414
  - lib/aspera/hash_ext.rb
396
415
  - lib/aspera/id_generator.rb
397
- - lib/aspera/json_rpc.rb
416
+ - lib/aspera/json_rpc/client.rb
417
+ - lib/aspera/json_rpc/version.rb
398
418
  - lib/aspera/keychain/base.rb
399
419
  - lib/aspera/keychain/encrypted_hash.rb
400
420
  - lib/aspera/keychain/factory.rb
401
421
  - lib/aspera/keychain/hashicorp_vault.rb
402
422
  - lib/aspera/keychain/macos_security.rb
403
423
  - lib/aspera/line_logger.rb
424
+ - lib/aspera/link_header.rb
404
425
  - lib/aspera/log.rb
405
426
  - lib/aspera/markdown.rb
406
427
  - lib/aspera/nagios.rb
@@ -451,6 +472,7 @@ files:
451
472
  - lib/aspera/transfer/error.rb
452
473
  - lib/aspera/transfer/faux_file.rb
453
474
  - lib/aspera/transfer/parameters.rb
475
+ - lib/aspera/transfer/result.rb
454
476
  - lib/aspera/transfer/resumer.rb
455
477
  - lib/aspera/transfer/spec.rb
456
478
  - lib/aspera/transfer/spec.schema.yaml
metadata.gz.sig CHANGED
Binary file
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # cspell:ignore blankslate
4
-
5
- require 'aspera/rest_error_analyzer'
6
- require 'aspera/assert'
7
- require 'blankslate'
8
-
9
- Aspera::RestErrorAnalyzer.instance.add_simple_handler(name: 'JSON RPC', path: %w[error message], always: true)
10
-
11
- module Aspera
12
- # a very simple JSON RPC client
13
- class JsonRpcClient < BlankSlate
14
- JSON_RPC_VERSION = '2.0'
15
- reveal :instance_variable_get
16
- reveal :inspect
17
- reveal :to_s
18
-
19
- def initialize(api, namespace = nil)
20
- super()
21
- @api = api
22
- @namespace = namespace
23
- @request_id = 0
24
- end
25
-
26
- def respond_to_missing?(_sym, _include_private = false)
27
- true
28
- end
29
-
30
- def method_missing(method, *args, &block)
31
- args = args.first if args.size == 1 && args.first.is_a?(Hash)
32
- data = @api.create('', {
33
- jsonrpc: JSON_RPC_VERSION,
34
- method: "#{@namespace}#{method}",
35
- params: args,
36
- id: @request_id += 1
37
- })
38
- Aspera.assert_type(data, Hash){'response'}
39
- Aspera.assert(data['jsonrpc'] == JSON_RPC_VERSION, 'bad version in response')
40
- Aspera.assert(data.key?('id'), 'missing id in response')
41
- Aspera.assert(!(data.key?('error') && data.key?('result')), 'both error and response')
42
- Aspera.assert(
43
- !data.key?('error') ||
44
- data['error'].is_a?(Hash) &&
45
- data['error']['code'].is_a?(Integer) &&
46
- data['error']['message'].is_a?(String),
47
- 'bad error response'
48
- )
49
- return data['result']
50
- end
51
- end
52
- end