unimatrix-cli 2.3.0 → 2.4.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/unimatrix_cli.rb +11 -29
  4. data/lib/unimatrix_cli/command.rb +0 -1
  5. data/lib/unimatrix_cli/config/acceptance.yml +2 -2
  6. data/lib/unimatrix_cli/config/configuration.rb +1 -1
  7. data/lib/unimatrix_cli/config/production.yml +2 -2
  8. data/lib/unimatrix_cli/config/staging.yml +2 -2
  9. data/lib/unimatrix_cli/iris/stream_converter/create_command.rb +48 -0
  10. data/lib/unimatrix_cli/iris/stream_converter/describe_command.rb +37 -0
  11. data/lib/unimatrix_cli/keymaker/policy/destroy_command.rb +69 -0
  12. data/lib/unimatrix_cli/keymaker/policy/write_command.rb +156 -0
  13. data/lib/unimatrix_cli/keymaker/resource/list_command.rb +37 -0
  14. data/lib/unimatrix_cli/keymaker/resource_owner/find_command.rb +76 -0
  15. data/lib/unimatrix_cli/keymaker/resource_owner/invite_command.rb +60 -0
  16. data/lib/unimatrix_cli/keymaker/resource_owner/policies_command.rb +66 -0
  17. data/lib/unimatrix_cli/keymaker/resource_owner/remove_command.rb +92 -0
  18. data/lib/unimatrix_cli/keymaker/resource_server/list_command.rb +32 -0
  19. data/lib/unimatrix_cli/login_command.rb +1 -1
  20. data/lib/unimatrix_cli/version.rb +1 -1
  21. data/unimatrix-cli.gemspec +1 -1
  22. metadata +15 -31
  23. data/lib/unimatrix_cli/citadel/app/build_command.rb +0 -28
  24. data/lib/unimatrix_cli/citadel/app/console_command.rb +0 -17
  25. data/lib/unimatrix_cli/citadel/app/db_setup_command.rb +0 -17
  26. data/lib/unimatrix_cli/citadel/app/environment_command.rb +0 -30
  27. data/lib/unimatrix_cli/citadel/app/logs_command.rb +0 -17
  28. data/lib/unimatrix_cli/citadel/app/migrate_command.rb +0 -17
  29. data/lib/unimatrix_cli/citadel/app/migrate_status_command.rb +0 -17
  30. data/lib/unimatrix_cli/citadel/app/rake_command.rb +0 -18
  31. data/lib/unimatrix_cli/citadel/app/routes_command.rb +0 -17
  32. data/lib/unimatrix_cli/citadel/citadel_command.rb +0 -285
  33. data/lib/unimatrix_cli/citadel/instance/details_command.rb +0 -18
  34. data/lib/unimatrix_cli/citadel/instance/scp_command.rb +0 -48
  35. data/lib/unimatrix_cli/citadel/instance/ssh_command.rb +0 -29
  36. data/lib/unimatrix_cli/citadel/instance/status_command.rb +0 -17
  37. data/lib/unimatrix_cli/citadel/instance/user_data_command.rb +0 -17
  38. data/lib/unimatrix_cli/citadel/instance/user_data_logs_command.rb +0 -17
  39. data/lib/unimatrix_cli/zephyrus/input/create_command.rb +0 -40
  40. data/lib/unimatrix_cli/zephyrus/input/describe_command.rb +0 -35
  41. data/lib/unimatrix_cli/zephyrus/input/list_command.rb +0 -38
  42. data/lib/unimatrix_cli/zephyrus/output/list_command.rb +0 -44
  43. data/lib/unimatrix_cli/zephyrus/recording/configuration_command.rb +0 -43
  44. data/lib/unimatrix_cli/zephyrus/rendition/list_command.rb +0 -40
  45. data/lib/unimatrix_cli/zephyrus/routing/configuration_command.rb +0 -43
  46. data/lib/unimatrix_cli/zephyrus/transcoding/configuration_command.rb +0 -55
  47. data/lib/unimatrix_cli/zephyrus/transcribing/configuration_command.rb +0 -55
  48. data/lib/unimatrix_cli/zephyrus/transmutation/configuration_command.rb +0 -55
@@ -0,0 +1,37 @@
1
+ module UnimatrixCLI
2
+ module Keymaker
3
+ module Resource
4
+ class ListCommand < Command
5
+
6
+ option :resource_server_uuid, "Resource server UUID to list resources", \
7
+ type: :string, required: true
8
+
9
+ synopsis "Lists resources for the given resource server"
10
+
11
+ def execute
12
+ endpoint = "#{ Configuration.default_config[ 'keymaker_url' ] }/" +
13
+ "resources?access_token=#{ Configuration.access_token }&" +
14
+ "resource_server_uuid=#{ @options[ :resource_server_uuid ] }"
15
+
16
+ resources_response = make_request( endpoint, 'Get' )
17
+
18
+ if resources_response.is_a?( Array )
19
+ resources_response.each do | resource |
20
+ write(
21
+ message: "Name: #{ resource[ 'name' ] }\n" +
22
+ "Code Name: #{ resource[ 'code_name' ] }\n" +
23
+ "Actions: #{ resource[ 'actions' ] }\n" +
24
+ "UUID: #{ resource[ 'uuid' ] }\n\n"
25
+ )
26
+ end
27
+ else
28
+ write(
29
+ message: "Error retrieving resources: " +
30
+ "#{ resources_response.inspect }", error: true
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,76 @@
1
+ module UnimatrixCLI
2
+ module Keymaker
3
+ module ResourceOwner
4
+ class FindCommand < Command
5
+
6
+ option :email_address, "Email address of resource owner being requested", \
7
+ type: :string
8
+
9
+ option :resource_owner_uuid, "UUID of resource owner being requested", \
10
+ type: :string
11
+
12
+ synopsis "Find an existing resource owner by email address or by UUID"
13
+
14
+ def execute
15
+ if ( @options[ :email_address ] && @options[ :resource_owner_uuid ].nil? ) ||
16
+ ( @options[ :email_address ].nil? && @options[ :resource_owner_uuid ] )
17
+
18
+ resource_owner_response = resource_owner_request
19
+
20
+ if !resource_owner_response[ 'error' ]
21
+ write(
22
+ message: "Resource Owner found. \n" +
23
+ "Email Address: #{ resource_owner_response[ 'email_address' ] } \n" +
24
+ "UUID: #{ resource_owner_response[ 'uuid' ] } \n" +
25
+ "First Name: #{ resource_owner_response[ 'name_first' ] || '<none>' } \n" +
26
+ "Last Name: #{ resource_owner_response[ 'name_last' ] || '<none>' }"
27
+ )
28
+ else
29
+ write(
30
+ message: "Error: unable to retrieve resource owner: " +
31
+ "#{ resource_owner_response.inspect }", error: true
32
+ )
33
+ end
34
+ else
35
+ write(
36
+ message: "Error: either option --email-address OR --resource-owner-uuid must be specified.",
37
+ error: true
38
+ )
39
+ end
40
+ end
41
+
42
+ #-----------------------------------------------------------------------
43
+
44
+ private; def encoded_query
45
+ URI.encode_www_form( { access_token: Configuration.access_token }.merge( option ) )
46
+ end
47
+
48
+ private; def option
49
+ if @options[ :email_address ]
50
+ { email_address: @options[ :email_address ] }
51
+ else
52
+ { uuid: @options[ :resource_owner_uuid ] }
53
+ end
54
+ end
55
+
56
+ private; def resource_owner_request
57
+ endpoint = "#{ Configuration.default_config[ 'keymaker_url' ] }/" +
58
+ "resource_owners?#{ encoded_query }"
59
+
60
+ response = make_request( endpoint, 'Get' )
61
+
62
+ if response.present?
63
+ response = response.first
64
+ else
65
+ response = {
66
+ 'error' => 'not_found',
67
+ 'error_description' => 'A resource owner with the given email address or uuid not found.'
68
+ }
69
+ end
70
+
71
+ response
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,60 @@
1
+ module UnimatrixCLI
2
+ module Keymaker
3
+ module ResourceOwner
4
+ class InviteCommand < Command
5
+
6
+ option :email_address, "Email address of resource owner being invited", \
7
+ type: :string, required: true
8
+
9
+ option :client, "Client for the authorization realm to invite the resource owner", \
10
+ type: :string, required: true
11
+
12
+ option :first_name, "First name of resource owner being invited", type: :string
13
+
14
+ option :last_name, "Last name of resource owner being invited", type: :string
15
+
16
+ synopsis "Invite a new resource owner"
17
+
18
+ def execute
19
+ endpoint = "#{ Configuration.default_config[ 'keymaker_url' ] }/invite?#{ encoded_query }"
20
+
21
+ resource_owner_response = make_request( endpoint, 'Post', { resource_owner: resource_owner } )
22
+
23
+ if !resource_owner_response[ 'error' ]
24
+ write(
25
+ message: "Invite successfully created for resource owner. \n" +
26
+ "Email Address: #{ resource_owner_response[ 'email_address' ] } \n" +
27
+ "UUID: #{ resource_owner_response[ 'uuid' ] } \n" +
28
+ "First Name: #{ resource_owner_response[ 'name_first' ] || '<none>' } \n" +
29
+ "Last Name: #{ resource_owner_response[ 'name_last' ] || '<none>' }"
30
+ )
31
+ else
32
+ write(
33
+ message: "Error inviting resource owner: " +
34
+ "#{ resource_owner_response.inspect }", error: true
35
+ )
36
+ end
37
+ end
38
+
39
+ #----------------------------------------------------------------------------
40
+
41
+ private; def encoded_query
42
+ URI.encode_www_form(
43
+ {
44
+ access_token: Configuration.access_token,
45
+ client_id: @options[ :client ],
46
+ redirect_uri: Configuration.default_config[ 'portal_url' ]
47
+ }
48
+ )
49
+ end
50
+
51
+ private; def resource_owner
52
+ { name_first: @options[ :first_name ],
53
+ name_last: @options[ :last_name ],
54
+ email_address: @options[ :email_address ]
55
+ }.delete_if { | k, v | !v.present? }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,66 @@
1
+ module UnimatrixCLI
2
+ module Keymaker
3
+ module ResourceOwner
4
+ class PoliciesCommand < Command
5
+
6
+ option :resource_owner_uuid, "Resource owner uuid for which to request policies", \
7
+ type: :string, required: true
8
+
9
+ option :realm, "Realm uuid for which to scope the policies returned", \
10
+ type: :string
11
+
12
+ synopsis "Lists policies for the given resource owner UUID for the realms " +
13
+ "for which the requestor has a policies read policy"
14
+
15
+ def execute
16
+ endpoint = "#{ Configuration.default_config[ 'keymaker_url' ] }/policies?" +
17
+ "access_token=#{ Configuration.access_token }&" +
18
+ "resource_owner_uuid=#{ @options[ :resource_owner_uuid ] }"
19
+
20
+ policies_response = make_request( endpoint, 'Get' )
21
+
22
+ if !policies_response[ 'error' ]
23
+ unscoped_policies = policies_response[ 'policies' ]
24
+
25
+ if unscoped_policies.present?
26
+ scoped_policies = scope_policies( unscoped_policies )
27
+
28
+ if scoped_policies.present?
29
+ scoped_policies.each do | policy |
30
+ write(
31
+ message: "Resource: #{ policy[ 'resource' ] }\n" +
32
+ "Actions: #{ policy[ 'actions' ] }\n\n"
33
+ )
34
+ end
35
+ else
36
+ write(
37
+ message: "No policies found for resource owner with uuid " +
38
+ "#{ @options[ :resource_owner_uuid ] } for realm #{ @options[ :realm ] }"
39
+ )
40
+ end
41
+ else
42
+ write(
43
+ message: "No policies found for resource owner with uuid " +
44
+ "#{ @options[ :resource_owner_uuid ] }"
45
+ )
46
+ end
47
+ else
48
+ write(
49
+ message: "Error: error retrieving policies for resource owner " +
50
+ "uuid #{ @options[ :resource_owner_uuid ] }\n" +
51
+ "#{ policies_response.inspect }", error: true
52
+ )
53
+ end
54
+ end
55
+
56
+ #----------------------------------------------------------------------------
57
+
58
+ private; def scope_policies( policies_array )
59
+ return policies_array unless @options[ :realm ]
60
+
61
+ policies_array.select { | policy | policy[ 'realm_uuid' ] == @options[ :realm ] }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,92 @@
1
+ module UnimatrixCLI
2
+ module Keymaker
3
+ module ResourceOwner
4
+ class RemoveCommand < Command
5
+
6
+ option :resource_owner_uuid, "Resource owner uuid for which to remove from the realm", \
7
+ type: :string, required: true
8
+
9
+ option :realm, "Realm uuid from which to remove the resource owner", \
10
+ type: :string, required: true
11
+
12
+ synopsis "Removes resource owner completely from the given realm"
13
+
14
+ def execute
15
+ endpoint = "#{ Configuration.default_config[ 'keymaker_url' ] }/policies?" +
16
+ "access_token=#{ Configuration.access_token }&" +
17
+ "resource_owner_uuid=#{ @options[ :resource_owner_uuid ] }"
18
+
19
+ policies_response = make_request( endpoint, 'Get' )
20
+
21
+ if !policies_response[ 'error' ]
22
+ unscoped_policies = policies_response[ 'policies' ]
23
+
24
+ if unscoped_policies.present?
25
+ scoped_policies = scope_policies( unscoped_policies )
26
+
27
+ if scoped_policies.present?
28
+ destroyed_count = 0
29
+
30
+ scoped_policies.each do | policy |
31
+ body = {
32
+ policy: {
33
+ resource: policy[ 'resource' ]
34
+ }
35
+ }
36
+
37
+ delete_response = make_request( endpoint, 'Delete', body )
38
+
39
+ if delete_response.empty?
40
+ destroyed_count += 1
41
+ else
42
+ write(
43
+ message: "\n**Error removing policy**\n" +
44
+ "Realm UUID: #{ policy[ 'realm_uuid' ] }\n" +
45
+ "Resource: #{ policy[ 'resource' ] }\n" +
46
+ "Error: #{ delete_response.inspect }\n\n", error: true
47
+ )
48
+ end
49
+ end
50
+
51
+ if destroyed_count == scoped_policies.length
52
+ write(
53
+ message: "Successfully removed resource owner #{ @options[ :resource_owner_uuid ] } " +
54
+ "from realm #{ @options[ :realm ] }"
55
+ )
56
+ else
57
+ write(
58
+ message: "Error: unable to completely remove resource owner from realm " +
59
+ "#{ @options[ :realm ] }, #{ destroyed_count } out of #{ scoped_policies.length } " +
60
+ "policies were removed", error: true
61
+ )
62
+ end
63
+ else
64
+ write(
65
+ message: "No policies found for resource owner with uuid " +
66
+ "#{ @options[ :resource_owner_uuid ] } for realm #{ @options[ :realm ] }"
67
+ )
68
+ end
69
+ else
70
+ write(
71
+ message: "No policies found for resource owner with uuid " +
72
+ "#{ @options[ :resource_owner_uuid ] }"
73
+ )
74
+ end
75
+ else
76
+ write(
77
+ message: "Error: error retrieving policies for resource owner " +
78
+ "uuid #{ @options[ :resource_owner_uuid ] }\n" +
79
+ "#{ policies_response.inspect }", error: true
80
+ )
81
+ end
82
+ end
83
+
84
+ #----------------------------------------------------------------------------
85
+
86
+ private; def scope_policies( policies_array )
87
+ policies_array.select { | policy | policy[ 'realm_uuid' ] == @options[ :realm ] }
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,32 @@
1
+ module UnimatrixCLI
2
+ module Keymaker
3
+ module ResourceServer
4
+ class ListCommand < Command
5
+
6
+ synopsis "List all resource servers"
7
+
8
+ def execute
9
+ endpoint = "#{ Configuration.default_config[ 'keymaker_url' ] }/" +
10
+ "resource_servers?access_token=#{ Configuration.access_token }"
11
+
12
+ resource_servers_response = make_request( endpoint, 'Get' )
13
+
14
+ if resource_servers_response.is_a?( Array )
15
+ resource_servers_response.each do | resource_server |
16
+ write(
17
+ message: "Name: #{ resource_server[ 'name' ] }\n" +
18
+ "Code Name: #{ resource_server[ 'code_name' ] }\n" +
19
+ "UUID: #{ resource_server[ 'uuid' ] }\n\n"
20
+ )
21
+ end
22
+ else
23
+ write(
24
+ message: "Error: unable to retrieve resource servers: " +
25
+ "#{ resource_servers_response.inspect }", error: true
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -9,7 +9,7 @@ module UnimatrixCLI
9
9
  option :secret, "Client secret (optional)", type: :string
10
10
 
11
11
  synopsis "Log in with your username, password, and environment. " +
12
- "Specify region as 'us-east-1' or 'us-west-1'." +
12
+ "Specify region as 'us-east-1' or 'us-west-2'." +
13
13
  "Specify a client_id, or make sure to provide it as an ENV variable. " +
14
14
  "Specify a client_secret, if necessary, or provide as an ENV variable."
15
15
 
@@ -1,3 +1,3 @@
1
1
  module UnimatrixCLI
2
- VERSION = '2.3.0'
2
+ VERSION = '2.4.0'
3
3
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do | spec |
17
17
  spec.executables = spec.files.grep( %r{^exe/} ) { | f | File.basename( f ) }
18
18
  spec.require_paths = [ "lib" ]
19
19
 
20
- spec.add_runtime_dependency "unimatrix", "~> 2.3"
20
+ spec.add_runtime_dependency "unimatrix", "~> 2.7"
21
21
  spec.add_runtime_dependency "aws-sdk", "~> 2.10"
22
22
  spec.add_runtime_dependency "net-ssh", '~> 4.2', '>= 4.2.0'
23
23
  spec.add_runtime_dependency "tty-prompt", "~> 0.14.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unimatrix-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sportsrocket
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-09 00:00:00.000000000 Z
11
+ date: 2018-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: unimatrix
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.3'
19
+ version: '2.7'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.3'
26
+ version: '2.7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: aws-sdk
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -171,22 +171,6 @@ files:
171
171
  - lib/unimatrix_cli/archivist/blueprint/create_command.rb
172
172
  - lib/unimatrix_cli/cartographer/region/create_command.rb
173
173
  - lib/unimatrix_cli/cartographer/region/import_cities_command.rb
174
- - lib/unimatrix_cli/citadel/app/build_command.rb
175
- - lib/unimatrix_cli/citadel/app/console_command.rb
176
- - lib/unimatrix_cli/citadel/app/db_setup_command.rb
177
- - lib/unimatrix_cli/citadel/app/environment_command.rb
178
- - lib/unimatrix_cli/citadel/app/logs_command.rb
179
- - lib/unimatrix_cli/citadel/app/migrate_command.rb
180
- - lib/unimatrix_cli/citadel/app/migrate_status_command.rb
181
- - lib/unimatrix_cli/citadel/app/rake_command.rb
182
- - lib/unimatrix_cli/citadel/app/routes_command.rb
183
- - lib/unimatrix_cli/citadel/citadel_command.rb
184
- - lib/unimatrix_cli/citadel/instance/details_command.rb
185
- - lib/unimatrix_cli/citadel/instance/scp_command.rb
186
- - lib/unimatrix_cli/citadel/instance/ssh_command.rb
187
- - lib/unimatrix_cli/citadel/instance/status_command.rb
188
- - lib/unimatrix_cli/citadel/instance/user_data_command.rb
189
- - lib/unimatrix_cli/citadel/instance/user_data_logs_command.rb
190
174
  - lib/unimatrix_cli/cli.rb
191
175
  - lib/unimatrix_cli/command.rb
192
176
  - lib/unimatrix_cli/config/acceptance.yml
@@ -196,6 +180,8 @@ files:
196
180
  - lib/unimatrix_cli/config/unimatrix.rb
197
181
  - lib/unimatrix_cli/iris/stream/create_command.rb
198
182
  - lib/unimatrix_cli/iris/stream/describe_command.rb
183
+ - lib/unimatrix_cli/iris/stream_converter/create_command.rb
184
+ - lib/unimatrix_cli/iris/stream_converter/describe_command.rb
199
185
  - lib/unimatrix_cli/iris/stream_encoder/create_command.rb
200
186
  - lib/unimatrix_cli/iris/stream_encoder/describe_command.rb
201
187
  - lib/unimatrix_cli/iris/stream_input/create_command.rb
@@ -208,19 +194,17 @@ files:
208
194
  - lib/unimatrix_cli/iris/stream_transcriber/describe_command.rb
209
195
  - lib/unimatrix_cli/iris/stream_transmutator/create_command.rb
210
196
  - lib/unimatrix_cli/iris/stream_transmutator/describe_command.rb
197
+ - lib/unimatrix_cli/keymaker/policy/destroy_command.rb
198
+ - lib/unimatrix_cli/keymaker/policy/write_command.rb
199
+ - lib/unimatrix_cli/keymaker/resource/list_command.rb
200
+ - lib/unimatrix_cli/keymaker/resource_owner/find_command.rb
201
+ - lib/unimatrix_cli/keymaker/resource_owner/invite_command.rb
202
+ - lib/unimatrix_cli/keymaker/resource_owner/policies_command.rb
203
+ - lib/unimatrix_cli/keymaker/resource_owner/remove_command.rb
204
+ - lib/unimatrix_cli/keymaker/resource_server/list_command.rb
211
205
  - lib/unimatrix_cli/login_command.rb
212
206
  - lib/unimatrix_cli/logout_command.rb
213
207
  - lib/unimatrix_cli/version.rb
214
- - lib/unimatrix_cli/zephyrus/input/create_command.rb
215
- - lib/unimatrix_cli/zephyrus/input/describe_command.rb
216
- - lib/unimatrix_cli/zephyrus/input/list_command.rb
217
- - lib/unimatrix_cli/zephyrus/output/list_command.rb
218
- - lib/unimatrix_cli/zephyrus/recording/configuration_command.rb
219
- - lib/unimatrix_cli/zephyrus/rendition/list_command.rb
220
- - lib/unimatrix_cli/zephyrus/routing/configuration_command.rb
221
- - lib/unimatrix_cli/zephyrus/transcoding/configuration_command.rb
222
- - lib/unimatrix_cli/zephyrus/transcribing/configuration_command.rb
223
- - lib/unimatrix_cli/zephyrus/transmutation/configuration_command.rb
224
208
  - lib/unimatrix_parser.rb
225
209
  - unimatrix-cli.gemspec
226
210
  homepage:
@@ -242,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
226
  version: '0'
243
227
  requirements: []
244
228
  rubyforge_project:
245
- rubygems_version: 2.5.2
229
+ rubygems_version: 2.4.8
246
230
  signing_key:
247
231
  specification_version: 4
248
232
  summary: Unimatrix CLI