ratproto 0.1.2 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7897b08049db9ed366fe7bace08dc7fba37089accf140df8a266071c1d30c676
4
- data.tar.gz: e8a7d6779e21941e3fb02a6c54683a22bb3d8bd65be4bef10d64507c86f193fe
3
+ metadata.gz: 9ae75289d3bfe8a1adb0a27e3f33111b194756524fc6b040bb7131cd16fe0b6a
4
+ data.tar.gz: 0a424e54c22bb32ffcfa5ee69f078b12705d0c19c3413a16867b9f4fc331d1a5
5
5
  SHA512:
6
- metadata.gz: 847b25a95b46ad7b07898790205e5e87761746b96c9488554548f27b74052f9086374e896a12ba8562d55f3560a0139d91ebf67eac495161bec27590ac5e4b67
7
- data.tar.gz: cdb37c2c7d782977c75207ad17cfed669e183beb73b6fb71f8fe9cc135516879aac8966814afd559236be1d1ad58907e822b296069c9a1f059fd5916cc3d17a3
6
+ metadata.gz: 9f1e9a749a2d46ba339a194f5331569b89d05480f419b84a208062b39306893bd2bc88139f144426330c234ff5cdc63303a728ce3d47228bd664e8e59d7ec600
7
+ data.tar.gz: '08d7134fd2647848dfd1df605c05b8b2b03893d0be2b2ffd0d55da50d36fd6168679b292765283231693fbeba80883f26a5464d6098a9f49cdd9853a5ef1c770'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.2.0] - 2026-07-28
2
+
3
+ - converted the codebase from a single script in `exe/rat` to per-command classes using Clamp framework
4
+
1
5
  ## [0.1.2] - 2026-05-24
2
6
 
3
7
  - `resolve`: added `-d` and `-p` options to print only the DID or the PDS endpoint respectively
data/README.md CHANGED
@@ -14,7 +14,7 @@ It builds on top of the existing ATProto Ruby gems:
14
14
 
15
15
  ## Installation
16
16
 
17
- To run this tool, you need some reasonably recent version of Ruby installed – it should run on Ruby 2.6 and above, although it's recommended to use a version that still gets maintainance updates, i.e. currently 3.2+.
17
+ To run this tool, you need some reasonably recent version of Ruby installed – it should run on Ruby 2.6 and above, although it's recommended to use a version that still gets maintainance updates, i.e. currently 3.3+.
18
18
 
19
19
  An older version of Ruby (2.6.x) that should work is shipped with macOS versions 11.0 or later, a recent version of Ruby is also likely to be already installed on most Linux systems, or at least available through the OS's package manager. More installation options can be found on [ruby-lang.org](https://www.ruby-lang.org/en/downloads/).
20
20
 
data/exe/rat CHANGED
@@ -2,319 +2,6 @@
2
2
 
3
3
  $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
4
4
 
5
- require 'didkit'
6
- require 'json'
7
- require 'minisky'
8
- require 'optparse'
9
- require 'time'
10
- require 'uri'
5
+ require 'ratproto/cli'
11
6
 
12
- require 'ratproto/version'
13
-
14
- DID_REGEXP = /\Adid:[a-z]+:[a-zA-Z0-9.\-_]+\z/
15
- NSID_REGEXP = /\A[a-z0-9]+(\.[a-z0-9]+)+\z/
16
- HANDLE_REGEXP = /\A[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)+\z/
17
-
18
- def print_help
19
- puts <<~HELP
20
- rat #{RatProto::VERSION} 🐀
21
-
22
- Usage:
23
- rat fetch at://uri
24
- rat stream <firehose-host> [options]
25
- rat resolve <did>|<handle>
26
-
27
- Commands:
28
- fetch Fetch a single record given its at:// URI
29
- stream Stream events from a relay / PDS firehose
30
- resolve Resolve a DID or @handle
31
- help Show this help
32
- version Show version
33
-
34
- Stream options:
35
- -j, --jetstream Use a Jetstream source instead of a CBOR firehose
36
- -r, --cursor CURSOR Start from a given cursor
37
- -d, --did DID[,DID...] Filter only events from given DID(s)
38
- (can be passed multiple times)
39
- -c, --collection NSID[,NSID...] Filter only events of given collection(s)
40
- (can be passed multiple times)
41
- HELP
42
- end
43
-
44
- def abort_with_error(message)
45
- puts message
46
- exit 1
47
- end
48
-
49
- def check_stream_did(did)
50
- if did =~ DID_REGEXP
51
- did
52
- elsif did =~ HANDLE_REGEXP
53
- if resolved_did = DID.resolve_handle(did)
54
- resolved_did.to_s
55
- else
56
- abort_with_error "Error: couldn't resolve handle @#{did}"
57
- end
58
- else
59
- abort_with_error "Error: #{did.inspect} is not a valid DID"
60
- end
61
- end
62
-
63
- def validate_nsid(collection)
64
- unless collection =~ NSID_REGEXP
65
- abort_with_error "Error: #{collection.inspect} is not a valid collection NSID"
66
- end
67
- end
68
-
69
- def parse_at_uri(uri)
70
- unless uri.start_with?('at://')
71
- abort_with_error "Error: not an at:// URI: #{uri.inspect}"
72
- end
73
-
74
- unless uri =~ /\Aat:\/\/([^\/]+)\/([^\/]+)\/([^\/]+)\z/
75
- abort_with_error "Error: invalid at:// URI: #{uri.inspect}"
76
- end
77
-
78
- [$1, $2, $3]
79
- end
80
-
81
- def run_fetch(args)
82
- uri = args.shift
83
-
84
- if uri.nil?
85
- abort_with_error "Usage: #{$PROGRAM_NAME} fetch <at://uri>"
86
- end
87
-
88
- if !args.empty?
89
- abort_with_error "Error: Unexpected arguments for fetch: #{args.join(' ')}"
90
- end
91
-
92
- repo, collection, rkey = parse_at_uri(uri)
93
-
94
- begin
95
- pds = DID.new(repo).document.pds_host
96
- sky = Minisky.new(pds, nil)
97
-
98
- response = sky.get_request('com.atproto.repo.getRecord', {
99
- repo: repo, collection: collection, rkey: rkey
100
- })
101
- rescue StandardError => e
102
- abort_with_error "Error loading record: #{e.class}: #{e.message}"
103
- end
104
-
105
- puts JSON.pretty_generate(response['value'])
106
- end
107
-
108
- def parse_resolve_options(args)
109
- options = {}
110
-
111
- parser = OptionParser.new do |opts|
112
- opts.banner = "Usage: #{$PROGRAM_NAME} resolve <did>|<handle> [options]"
113
-
114
- opts.on('-d', '--did', 'Print only the DID resolved from the handle') do
115
- options[:only_did] = true
116
- end
117
-
118
- opts.on('-p', '--pds', 'Print only the PDS endpoint') do
119
- options[:only_pds] = true
120
- end
121
-
122
- opts.on('-h', '--help', 'Show resolve-specific help') do
123
- puts opts
124
- exit
125
- end
126
- end
127
-
128
- remaining = []
129
-
130
- begin
131
- parser.order!(args) { |other| remaining << other }
132
- rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
133
- puts "Error: #{e.message}"
134
- puts parser
135
- exit 1
136
- end
137
-
138
- if options[:only_did] && options[:only_pds]
139
- abort_with_error "--pds and --did options cannot be used together"
140
- end
141
-
142
- [options, remaining]
143
- end
144
-
145
- def run_resolve(args)
146
- options, arguments = parse_resolve_options(args)
147
- target = arguments.shift
148
-
149
- if target.nil?
150
- abort_with_error "Usage: #{$PROGRAM_NAME} resolve <did>|<handle> [options]"
151
- end
152
-
153
- if !args.empty?
154
- abort_with_error "Error: Unexpected arguments for resolve: #{args.join(' ')}"
155
- end
156
-
157
- did = DID.resolve_handle(target)
158
-
159
- if did.nil?
160
- abort_with_error "Couldn't resolve #{target}"
161
- end
162
-
163
- if options[:only_did]
164
- puts did
165
- elsif options[:only_pds]
166
- puts did.document.pds_endpoint
167
- else
168
- puts JSON.pretty_generate(did.document.json)
169
- end
170
- rescue StandardError => e
171
- abort_with_error "Error resolving #{target.inspect}: #{e.class}: #{e.message}"
172
- end
173
-
174
- def parse_stream_options(args)
175
- options = {}
176
-
177
- parser = OptionParser.new do |opts|
178
- opts.banner = "Usage: #{$PROGRAM_NAME} stream <relay.host> [options]"
179
-
180
- opts.on('-j', '--jetstream', 'Use a Jetstream source') do
181
- options[:jetstream] = true
182
- end
183
-
184
- opts.on('-rCURSOR', '--cursor=CURSOR', 'Start from a given cursor') do |cursor|
185
- options[:cursor] = cursor
186
- end
187
-
188
- opts.on('-dLIST', '--did=LIST', 'Filter only events from given DID(s) (comma-separated or repeated)') do |list|
189
- items = list.split(',').map(&:strip).reject(&:empty?)
190
-
191
- if items.empty?
192
- abort_with_error "Error: empty argument to -d/--did"
193
- end
194
-
195
- options[:dids] ||= []
196
- options[:dids] += items
197
- end
198
-
199
- opts.on('-cLIST', '--collection=LIST', 'Filter only events of given collections') do |list|
200
- items = list.split(',').map(&:strip).reject(&:empty?)
201
-
202
- if items.empty?
203
- abort_with_error "Error: empty argument to -c/--collection"
204
- end
205
-
206
- options[:collections] ||= []
207
- options[:collections] += items
208
- end
209
-
210
- opts.on('-h', '--help', 'Show stream-specific help') do
211
- puts opts
212
- exit
213
- end
214
- end
215
-
216
- remaining = []
217
-
218
- begin
219
- parser.order!(args) { |other| remaining << other }
220
- rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
221
- puts "Error: #{e.message}"
222
- puts parser
223
- exit 1
224
- end
225
-
226
- [options, remaining]
227
- end
228
-
229
- def run_stream(args)
230
- options, arguments = parse_stream_options(args)
231
-
232
- service = arguments.shift
233
-
234
- if service.nil?
235
- abort_with_error "Usage: #{$PROGRAM_NAME} stream <firehose-host> [options]"
236
- end
237
-
238
- if !arguments.empty?
239
- abort_with_error "Error: Unexpected arguments for stream: #{arguments.join(' ')}"
240
- end
241
-
242
- if options[:cursor] && options[:cursor] !~ /\A\d+\z/
243
- abort_with_error "Error: cursor must be a decimal integer, got: #{options[:cursor].inspect}"
244
- end
245
-
246
- if options[:dids]
247
- options[:dids] = options[:dids].map { |did| check_stream_did(did) }
248
- end
249
-
250
- if options[:collections]
251
- options[:collections].each { |collection| validate_nsid(collection) }
252
- end
253
-
254
- if options[:jetstream]
255
- jet_opts = {}
256
- jet_opts[:cursor] = options[:cursor].to_i if options[:cursor]
257
-
258
- # pass DID/collection filters to Jetstream to filter events server-side
259
- jet_opts[:wanted_dids] = options[:dids] if options[:dids]
260
- jet_opts[:wanted_collections] = options[:collections] if options[:collections]
261
-
262
- sky = Skyfall::Jetstream.new(service, jet_opts)
263
- else
264
- cursor = options[:cursor]&.to_i
265
- sky = Skyfall::Firehose.new(service, :subscribe_repos, cursor)
266
- end
267
-
268
- sky.on_connecting { |url| puts "Connecting to #{url}..." }
269
- sky.on_connect { puts "Connected" }
270
- sky.on_disconnect { puts "Disconnected" }
271
- sky.on_reconnect { puts "Connection lost, trying to reconnect..." }
272
- sky.on_timeout { puts "Connection stalled, triggering a reconnect..." }
273
- sky.on_error { |e| puts "ERROR: #{e}" }
274
-
275
- sky.on_message do |msg|
276
- next unless msg.type == :commit
277
- next if options[:dids] && !options[:dids].include?(msg.repo)
278
-
279
- time = msg.time.getlocal.iso8601
280
-
281
- msg.operations.each do |op|
282
- next if options[:collections] && !options[:collections].include?(op.collection)
283
-
284
- json = op.raw_record && JSON.generate(op.raw_record)
285
- puts "[#{time}] #{msg.repo} #{op.action.inspect} #{op.collection} #{op.rkey} #{json}"
286
- end
287
- end
288
-
289
- trap('SIGINT') do
290
- puts 'Disconnecting...'
291
- sky.disconnect
292
- end
293
-
294
- sky.connect
295
-
296
- puts "Stopped stream at cursor #{sky.cursor}"
297
- end
298
-
299
- if ARGV.empty?
300
- print_help
301
- exit
302
- end
303
-
304
- cmd = ARGV.shift
305
-
306
- case cmd
307
- when 'help', '--help', '-h'
308
- print_help
309
- when 'version', '--version'
310
- puts "RatProto #{RatProto::VERSION} 🐀"
311
- when 'fetch'
312
- run_fetch(ARGV)
313
- when 'resolve'
314
- run_resolve(ARGV)
315
- when 'stream'
316
- require 'skyfall'
317
- run_stream(ARGV)
318
- else
319
- abort_with_error "Error: unknown command: #{cmd}"
320
- end
7
+ RatProto::CLI.run
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clamp'
4
+
5
+ require_relative 'version'
6
+ require_relative 'commands/base'
7
+ require_relative 'commands/fetch'
8
+ require_relative 'commands/resolve'
9
+ require_relative 'commands/stream'
10
+
11
+ Clamp.allow_options_after_parameters = true
12
+
13
+ module RatProto
14
+ module Commands
15
+ class Version < Base
16
+ banner 'Show version'
17
+
18
+ def execute
19
+ puts "RatProto #{RatProto::VERSION} 🐀"
20
+ end
21
+ end
22
+
23
+ class Help < Base
24
+ banner 'Show help for rat or one of its commands'
25
+
26
+ parameter '[COMMAND]', 'command to describe'
27
+
28
+ def execute
29
+ program_name = File.basename($PROGRAM_NAME)
30
+
31
+ if command
32
+ command_class = RatProto::CLI.find_subcommand_class(command)
33
+ signal_usage_error "unknown command: #{command}" unless command_class
34
+
35
+ puts command_class.help("#{program_name} #{command}")
36
+ else
37
+ puts RatProto::CLI.help(program_name)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ class CLI < Commands::Base
44
+ banner "RatProto #{RatProto::VERSION} 🐀 — Ruby ATProto CLI"
45
+
46
+ option '--version', :flag, 'show version' do
47
+ Commands::Version.run("", [])
48
+ exit
49
+ end
50
+
51
+ subcommand 'fetch', 'fetch a single record given its at:// URI', Commands::Fetch
52
+ subcommand 'stream', 'stream events from a relay or PDS firehose', Commands::Stream
53
+ subcommand 'resolve', 'resolve a DID or @handle', Commands::Resolve
54
+ subcommand 'help', 'show help', Commands::Help
55
+ subcommand 'version', 'show version', Commands::Version
56
+ end
57
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clamp'
4
+
5
+ module RatProto
6
+ module Commands
7
+ class Base < Clamp::Command
8
+ DID_REGEXP = /\Adid:[a-z]+:[a-zA-Z0-9.\-_]+\z/
9
+ NSID_REGEXP = /\A[a-z0-9]+(\.[a-z0-9]+)+\z/
10
+ HANDLE_REGEXP = /\A[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)+\z/
11
+
12
+ private
13
+
14
+ def parse_at_uri(value)
15
+ signal_usage_error "not an at:// URI: #{value.inspect}" unless value.start_with?('at://')
16
+
17
+ match = value.match(/\Aat:\/\/([^\/]+)\/([^\/]+)\/([^\/]+)\z/)
18
+ signal_usage_error "invalid at:// URI: #{value.inspect}" unless match
19
+
20
+ match.captures
21
+ end
22
+
23
+ def comma_separated(values, option_name)
24
+ values = (values || []).map { |value|
25
+ args = value.split(',').map(&:strip).reject(&:empty?)
26
+ signal_usage_error "empty argument to #{option_name}" if args.empty?
27
+ args
28
+ }
29
+
30
+ values.flatten
31
+ end
32
+
33
+ def map_argument_to_did(did)
34
+ if did =~ DID_REGEXP
35
+ did
36
+ elsif did =~ HANDLE_REGEXP
37
+ if resolved_did = DID.resolve_handle(did)
38
+ resolved_did.to_s
39
+ else
40
+ signal_error "couldn't resolve handle @#{did}"
41
+ end
42
+ else
43
+ signal_usage_error "#{did.inspect} is not a valid DID"
44
+ end
45
+ end
46
+
47
+ def validate_nsid(collection)
48
+ return collection if collection =~ NSID_REGEXP
49
+
50
+ signal_usage_error "#{collection.inspect} is not a valid collection NSID"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'didkit'
4
+ require 'json'
5
+
6
+ require_relative 'base'
7
+
8
+ module RatProto
9
+ module Commands
10
+ class Fetch < Base
11
+ banner 'Fetch a single record given its at:// URI'
12
+
13
+ parameter 'URI', 'ATProto record URI'
14
+
15
+ def execute
16
+ require 'minisky'
17
+
18
+ repo, collection, rkey = parse_at_uri(uri)
19
+ response = load_record(repo, collection, rkey)
20
+ puts JSON.pretty_generate(response['value'])
21
+ end
22
+
23
+
24
+ private
25
+
26
+ def load_record(repo, collection, rkey)
27
+ pds = DID.new(repo).document.pds_host
28
+ sky = Minisky.new(pds, nil)
29
+
30
+ sky.get_request('com.atproto.repo.getRecord', {
31
+ repo: repo, collection: collection, rkey: rkey
32
+ })
33
+ rescue StandardError => e
34
+ signal_error "loading record: #{e.class}: #{e.message}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'didkit'
4
+ require 'json'
5
+
6
+ require_relative 'base'
7
+
8
+ module RatProto
9
+ module Commands
10
+ class Resolve < Base
11
+ banner 'Resolve a DID or @handle'
12
+
13
+ parameter 'TARGET', 'DID or handle'
14
+
15
+ option ['-d', '--did'], :flag, 'print only the DID resolved from the handle'
16
+ option ['-p', '--pds'], :flag, 'print only the PDS endpoint'
17
+
18
+ def execute
19
+ signal_usage_error '--pds and --did options cannot be used together' if did? && pds?
20
+
21
+ begin
22
+ resolved_did = DID.resolve_handle(target)
23
+ rescue StandardError => e
24
+ signal_error "resolving #{target.inspect}: #{e.class}: #{e.message}"
25
+ end
26
+
27
+ if resolved_did.nil?
28
+ signal_error "couldn't resolve #{target}"
29
+ end
30
+
31
+ if did?
32
+ puts resolved_did
33
+ elsif pds?
34
+ puts resolved_did.document.pds_endpoint
35
+ else
36
+ puts JSON.pretty_generate(resolved_did.document.json)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'didkit'
4
+ require 'json'
5
+ require 'time'
6
+
7
+ require_relative 'base'
8
+
9
+ module RatProto
10
+ module Commands
11
+ class Stream < Base
12
+ banner 'Stream events from a relay or PDS firehose'
13
+
14
+ parameter 'SERVICE', 'relay, PDS or Jetstream hostname'
15
+
16
+ option ['-j', '--jetstream'], :flag, 'use a Jetstream source'
17
+
18
+ option ['-r', '--cursor'], 'CURSOR', 'start from a given cursor' do |value|
19
+ raise ArgumentError, 'must be a decimal integer' unless value =~ /\A\d+\z/
20
+
21
+ value.to_i
22
+ end
23
+
24
+ option ['-d', '--did'], 'DID[,DID...]', 'filter events by DID (comma-separated or repeated)',
25
+ multivalued: true, attribute_name: :did_values
26
+
27
+ option ['-c', '--collection'], 'NSID[,NSID...]', 'filter events by collection (comma-separated or repeated)',
28
+ multivalued: true, attribute_name: :collection_values
29
+
30
+ def execute
31
+ require 'skyfall'
32
+
33
+ dids = comma_separated(did_values, '-d/--did')
34
+ collections = comma_separated(collection_values, '-c/--collection')
35
+
36
+ dids = (dids.length > 0) ? dids.map { |did| map_argument_to_did(did) } : nil
37
+ collections = (collections.length > 0) ? collections.map { |collection| validate_nsid(collection) } : nil
38
+
39
+ sky = build_stream(dids, collections)
40
+ configure_callbacks(sky, dids, collections)
41
+ configure_interrupt_handler(sky)
42
+
43
+ sky.connect
44
+
45
+ puts "Stopped stream at cursor #{sky.cursor}"
46
+ end
47
+
48
+
49
+ private
50
+
51
+ def build_stream(dids, collections)
52
+ if jetstream?
53
+ options = {}
54
+ options[:cursor] = cursor if cursor
55
+
56
+ # pass DID/collection filters to Jetstream to filter events server-side
57
+ options[:wanted_dids] = dids if dids
58
+ options[:wanted_collections] = collections if collections
59
+
60
+ Skyfall::Jetstream.new(service, options)
61
+ else
62
+ Skyfall::Firehose.new(service, :subscribe_repos, cursor)
63
+ end
64
+ end
65
+
66
+ def configure_callbacks(sky, dids, collections)
67
+ sky.on_connecting { |url| puts "Connecting to #{url}..." }
68
+ sky.on_connect { puts "Connected" }
69
+ sky.on_disconnect { puts "Disconnected" }
70
+ sky.on_reconnect { puts "Connection lost, trying to reconnect..." }
71
+ sky.on_timeout { puts "Connection stalled, triggering a reconnect..." }
72
+ sky.on_error { |e| puts "ERROR: #{e}" }
73
+
74
+ sky.on_message do |msg|
75
+ next unless msg.type == :commit
76
+ next if dids && !dids.include?(msg.repo)
77
+
78
+ time = msg.time.getlocal.iso8601
79
+
80
+ msg.operations.each do |op|
81
+ next if collections && !collections.include?(op.collection)
82
+
83
+ json = op.raw_record && JSON.generate(op.raw_record)
84
+ puts "[#{time}] (#{msg.seq}) #{msg.repo} #{op.action.inspect} #{op.collection} #{op.rkey} #{json}"
85
+ end
86
+ end
87
+ end
88
+
89
+ def configure_interrupt_handler(sky)
90
+ trap('SIGINT') do
91
+ puts
92
+ puts 'Disconnecting...'
93
+ sky.disconnect
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RatProto
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ratproto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuba Suder
@@ -69,6 +69,26 @@ dependencies:
69
69
  - - "<"
70
70
  - !ruby/object:Gem::Version
71
71
  version: '2.0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: clamp
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '1.5'
79
+ - - "<"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.0'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '1.5'
89
+ - - "<"
90
+ - !ruby/object:Gem::Version
91
+ version: '2.0'
72
92
  email:
73
93
  - jakub.suder@gmail.com
74
94
  executables:
@@ -81,6 +101,11 @@ files:
81
101
  - README.md
82
102
  - exe/rat
83
103
  - lib/ratproto.rb
104
+ - lib/ratproto/cli.rb
105
+ - lib/ratproto/commands/base.rb
106
+ - lib/ratproto/commands/fetch.rb
107
+ - lib/ratproto/commands/resolve.rb
108
+ - lib/ratproto/commands/stream.rb
84
109
  - lib/ratproto/version.rb
85
110
  - sig/ratproto.rbs
86
111
  homepage: https://ruby.sdk.blue
@@ -104,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
129
  - !ruby/object:Gem::Version
105
130
  version: '0'
106
131
  requirements: []
107
- rubygems_version: 4.0.10
132
+ rubygems_version: 3.6.9
108
133
  specification_version: 4
109
134
  summary: Ruby CLI tool for accessing Bluesky API / ATProto
110
135
  test_files: []