meshtastic 0.0.40 → 0.0.42
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 +4 -4
- data/Gemfile +3 -2
- data/lib/meshtastic/mqtt.rb +178 -109
- data/lib/meshtastic/version.rb +1 -1
- metadata +20 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ffe0a1e8528090281aedce5b132f1f57aa82db0c8c46131cff53d00004e4dcd
|
4
|
+
data.tar.gz: 56bb82c58cdcbced0c401802180931aa4625e4314e4954ab30349811a614d399
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd84aa7e0607992de00be53a0f8ccb2d69077004dd21ebc2838be38582064a797c0f13317c0abd375ab67199dd494640187895384ea74110c77c9b478a218654
|
7
|
+
data.tar.gz: a1135c741f44f38765421ea2020173b40395478fffdbb24da760d75f8483920f99cf2f0d110f7173fb9617e7522a644dc418f937aef79a35f1b04d89ed94737c
|
data/Gemfile
CHANGED
@@ -13,7 +13,7 @@ gemspec
|
|
13
13
|
# to review these custom flags (e.g. pg, serialport, etc).
|
14
14
|
gem 'bundler', '>=2.5.3'
|
15
15
|
gem 'bundle-audit', '0.1.0'
|
16
|
-
gem 'geocoder', '1.8.
|
16
|
+
gem 'geocoder', '1.8.3'
|
17
17
|
gem 'google-protobuf', '3.21.12'
|
18
18
|
gem 'mqtt', '0.6.0'
|
19
19
|
gem 'rake', '13.2.1'
|
@@ -21,6 +21,7 @@ gem 'rdoc', '6.6.3.1'
|
|
21
21
|
gem 'rspec', '3.13.0'
|
22
22
|
gem 'rubocop', '1.63.4'
|
23
23
|
gem 'rubocop-rake', '0.6.0'
|
24
|
-
gem 'rubocop-rspec', '2.29.
|
24
|
+
gem 'rubocop-rspec', '2.29.2'
|
25
25
|
gem 'rvm', '1.11.3.9'
|
26
|
+
gem 'tty-prompt', '0.23.1'
|
26
27
|
gem 'yard', '0.9.36'
|
data/lib/meshtastic/mqtt.rb
CHANGED
@@ -6,6 +6,7 @@ require 'json'
|
|
6
6
|
require 'mqtt'
|
7
7
|
require 'openssl'
|
8
8
|
require 'securerandom'
|
9
|
+
require 'tty-prompt'
|
9
10
|
|
10
11
|
# Avoiding Namespace Collisions
|
11
12
|
MQTTClient = MQTT::Client
|
@@ -42,16 +43,156 @@ module Meshtastic
|
|
42
43
|
raise e
|
43
44
|
end
|
44
45
|
|
46
|
+
# Supported Method Parameters::
|
47
|
+
# Meshtastic::MQQT.get_cipher_keys(
|
48
|
+
# psks: 'required - hash of channel / pre-shared key value pairs'
|
49
|
+
# )
|
50
|
+
|
51
|
+
private_class_method def self.get_cipher_keys(opts = {})
|
52
|
+
psks = opts[:psks]
|
53
|
+
|
54
|
+
psks.each_key do |key|
|
55
|
+
psk = psks[key]
|
56
|
+
padded_psk = psk.ljust(psk.length + ((4 - (psk.length % 4)) % 4), '=')
|
57
|
+
replaced_psk = padded_psk.gsub('-', '+').gsub('_', '/')
|
58
|
+
psks[key] = replaced_psk
|
59
|
+
end
|
60
|
+
|
61
|
+
psks
|
62
|
+
rescue StandardError => e
|
63
|
+
raise e
|
64
|
+
end
|
65
|
+
|
66
|
+
# Supported Method Parameters::
|
67
|
+
# Meshtastic::MQQT.recursively_decode_payloads(
|
68
|
+
# object: 'required - object to recursively decode',
|
69
|
+
# msg_type: 'required - message type (e.g. :TEXT_MESSAGE_APP)',
|
70
|
+
# gps_metadata: 'optional - include GPS metadata in output (default: false)'
|
71
|
+
# )
|
72
|
+
|
73
|
+
private_class_method def self.recursively_decode_payloads(opts = {})
|
74
|
+
object = opts[:object]
|
75
|
+
msg_type = opts[:msg_type]
|
76
|
+
gps_metadata = opts[:gps_metadata]
|
77
|
+
|
78
|
+
# puts "OBJECT: #{object.inspect}"
|
79
|
+
object = Meshtastic::Data.decode(object).to_h if object.is_a?(String)
|
80
|
+
|
81
|
+
object.each do |key, value|
|
82
|
+
if value.is_a?(Hash)
|
83
|
+
# Recursive call if the value is a hash
|
84
|
+
recursively_decode_payloads(object: value)
|
85
|
+
elsif value.is_a?(Array)
|
86
|
+
# Process each element if it's an array
|
87
|
+
value.each do |item|
|
88
|
+
recursively_decode_payloads(object: item) if item.is_a?(Hash)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# Check if the key matches 'payload' and decode
|
92
|
+
case key
|
93
|
+
when :latitude_i
|
94
|
+
object[:latitude] = object[:latitude_i] * 0.0000001
|
95
|
+
when :longitude_i
|
96
|
+
object[:longitude] = object[:longitude_i] * 0.0000001
|
97
|
+
when :macaddr
|
98
|
+
object[key] = object[key].bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join(':')
|
99
|
+
when :payload
|
100
|
+
case msg_type
|
101
|
+
when :ADMIN_APP
|
102
|
+
decoder = Meshtastic::AdminMessage.decode(payload)
|
103
|
+
when :ATAK_FORWARDER, :ATAK_PLUGIN
|
104
|
+
decoder = Meshtastic::TAKPacket
|
105
|
+
# when :AUDIO_APP
|
106
|
+
# decoder = Meshtastic::Audio
|
107
|
+
when :DETECTION_SENSOR_APP
|
108
|
+
decoder = Meshtastic::DeviceState
|
109
|
+
# when :IP_TUNNEL_APP
|
110
|
+
# decoder = Meshtastic::IpTunnel
|
111
|
+
when :MAP_REPORT_APP
|
112
|
+
decoder = Meshtastic::MapReport
|
113
|
+
# when :MAX
|
114
|
+
# decoder = Meshtastic::Max
|
115
|
+
when :NEIGHBORINFO_APP
|
116
|
+
decoder = Meshtastic::NeighborInfo
|
117
|
+
when :NODEINFO_APP
|
118
|
+
decoder = Meshtastic::User
|
119
|
+
when :PAXCOUNTER_APP
|
120
|
+
decoder = Meshtastic::Paxcount
|
121
|
+
when :POSITION_APP
|
122
|
+
decoder = Meshtastic::Position
|
123
|
+
# when :PRIVATE_APP
|
124
|
+
# decoder = Meshtastic::Private
|
125
|
+
when :RANGE_TEST_APP
|
126
|
+
# Unsure if this is the correct protobuf object
|
127
|
+
decoder = Meshtastic::FromRadio
|
128
|
+
when :REMOTE_HARDWARE_APP
|
129
|
+
decoder = Meshtastic::HardwareMessage
|
130
|
+
# when :REPLY_APP
|
131
|
+
# decoder = Meshtastic::Reply
|
132
|
+
when :ROUTING_APP
|
133
|
+
decoder = Meshtastic::Routing
|
134
|
+
when :SERIAL_APP
|
135
|
+
decoder = Meshtastic::SerialConnectionStatus
|
136
|
+
when :SIMULATOR_APP
|
137
|
+
decoder = Meshtastic::Compressed
|
138
|
+
when :STORE_FORWARD_APP
|
139
|
+
decoder = Meshtastic::StoreAndForward
|
140
|
+
when :TEXT_MESSAGE_APP
|
141
|
+
# Unsure if this is the correct protobuf object
|
142
|
+
# decoder = Meshtastic::MqttClientProxyMessage
|
143
|
+
decoder = Meshtastic::Data
|
144
|
+
when :TELEMETRY_APP
|
145
|
+
decoder = Meshtastic::Telemetry
|
146
|
+
when :TRACEROUTE_APP
|
147
|
+
decoder = Meshtastic::RouteDiscovery
|
148
|
+
when :UNKNOWN_APP
|
149
|
+
decoder = Meshtastic::Data.decode
|
150
|
+
when :WAYPOINT_APP
|
151
|
+
decoder = Meshtastic::Waypoint
|
152
|
+
# when :ZPS_APP
|
153
|
+
# decoder = Meshtastic::Zps
|
154
|
+
else
|
155
|
+
puts "WARNING: Unknown message type: #{msg_type}"
|
156
|
+
decoder = Meshtastic::Data.decode
|
157
|
+
end
|
158
|
+
|
159
|
+
# begin
|
160
|
+
# # If the value is base64 encoded, base64 decode and then decode
|
161
|
+
# base64_decoded_value = Base64.strict_decode64(value)
|
162
|
+
# object[key] = decoder.decode(base64_decoded_value).to_h
|
163
|
+
# rescue ArgumentError => e
|
164
|
+
# # Otherwise, just decode the value
|
165
|
+
object[key] = decoder.decode(value).to_h
|
166
|
+
# end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
next unless gps_metadata && object[:latitude] && object[:longitude]
|
171
|
+
|
172
|
+
object[:gps_metadata] = gps_search(
|
173
|
+
lat: object[:latitude],
|
174
|
+
lon: object[:longitude]
|
175
|
+
).first.data
|
176
|
+
end
|
177
|
+
|
178
|
+
object
|
179
|
+
rescue Google::Protobuf::ParseError
|
180
|
+
object
|
181
|
+
rescue StandardError => e
|
182
|
+
raise e
|
183
|
+
end
|
184
|
+
|
45
185
|
# Supported Method Parameters::
|
46
186
|
# Meshtastic::MQQT.subscribe(
|
47
187
|
# mqtt_obj: 'required - mqtt_obj returned from #connect method'
|
48
188
|
# root_topic: 'optional - root topic (default: msh)',
|
49
189
|
# region: 'optional - region e.g. 'US/VA', etc (default: US)',
|
50
190
|
# channel: 'optional - channel name e.g. "2/stat/#" (default: "2/e/LongFast/#")',
|
51
|
-
#
|
191
|
+
# psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: "AQ==" })',
|
52
192
|
# qos: 'optional - quality of service (default: 0)',
|
53
193
|
# filter: 'optional - comma-delimited string(s) to filter on in message (default: nil)',
|
54
|
-
# gps_metadata: 'optional - include GPS metadata in output (default: false)'
|
194
|
+
# gps_metadata: 'optional - include GPS metadata in output (default: false)',
|
195
|
+
# include_raw: 'optional - include raw packet data in output (default: false)'
|
55
196
|
# )
|
56
197
|
|
57
198
|
public_class_method def self.subscribe(opts = {})
|
@@ -60,34 +201,30 @@ module Meshtastic
|
|
60
201
|
region = opts[:region] ||= 'US'
|
61
202
|
channel = opts[:channel] ||= '2/e/LongFast/#'
|
62
203
|
# TODO: Support Array of PSKs and attempt each until decrypted
|
63
|
-
|
204
|
+
|
205
|
+
public_psk = '1PG7OiApB1nwvP+rz05pAQ=='
|
206
|
+
psks = opts[:psks] ||= { LongFast: public_psk }
|
207
|
+
raise 'ERROR: psks parameter must be a hash of :channel => psk key value pairs' unless psks.is_a?(Hash)
|
208
|
+
|
209
|
+
psks[:LongFast] = public_psk if psks[:LongFast] == 'AQ=='
|
210
|
+
psks = get_cipher_keys(psks: psks)
|
211
|
+
|
64
212
|
qos = opts[:qos] ||= 0
|
65
213
|
json = opts[:json] ||= false
|
66
214
|
filter = opts[:filter]
|
67
215
|
gps_metadata = opts[:gps_metadata] ||= false
|
216
|
+
include_raw = opts[:include_raw] ||= false
|
68
217
|
|
69
|
-
#
|
218
|
+
# NOTE: Use MQTT Explorer for topic discovery
|
70
219
|
full_topic = "#{root_topic}/#{region}/#{channel}"
|
71
220
|
puts "Subscribing to: #{full_topic}"
|
72
221
|
mqtt_obj.subscribe(full_topic, qos)
|
73
222
|
|
74
|
-
# Decrypt the message
|
75
|
-
# Our AES key is 128 or 256 bits, shared as part of the 'Channel' specification.
|
76
|
-
|
77
|
-
# Actual pre-shared key for LongFast channel
|
78
|
-
psk = '1PG7OiApB1nwvP+rz05pAQ==' if psk == 'AQ=='
|
79
|
-
padded_psk = psk.ljust(psk.length + ((4 - (psk.length % 4)) % 4), '=')
|
80
|
-
replaced_psk = padded_psk.gsub('-', '+').gsub('_', '/')
|
81
|
-
psk = replaced_psk
|
82
|
-
dec_psk = Base64.strict_decode64(psk)
|
83
|
-
|
84
|
-
# cipher = OpenSSL::Cipher.new('AES-256-CTR')
|
85
|
-
cipher = OpenSSL::Cipher.new('AES-128-CTR')
|
86
223
|
filter_arr = filter.to_s.split(',').map(&:strip)
|
87
224
|
mqtt_obj.get_packet do |packet_bytes|
|
88
|
-
|
225
|
+
raw_packet = packet_bytes.to_s.b if include_raw
|
89
226
|
raw_topic = packet_bytes.topic ||= ''
|
90
|
-
raw_payload = packet_bytes.payload
|
227
|
+
raw_payload = packet_bytes.payload ||= ''
|
91
228
|
|
92
229
|
begin
|
93
230
|
disp = false
|
@@ -107,116 +244,45 @@ module Meshtastic
|
|
107
244
|
message[:node_id_from] = "!#{message[:from].to_i.to_s(16)}"
|
108
245
|
message[:node_id_to] = "!#{message[:to].to_i.to_s(16)}"
|
109
246
|
|
247
|
+
# If encrypted_message is not nil, then decrypt
|
248
|
+
# the message prior to decoding.
|
110
249
|
encrypted_message = message[:encrypted]
|
111
|
-
# If encrypted_message is not nil, then decrypt the message
|
112
250
|
if encrypted_message.to_s.length.positive?
|
113
251
|
packet_id = message[:id]
|
114
252
|
packet_from = message[:from]
|
253
|
+
|
115
254
|
nonce_packet_id = [packet_id].pack('V').ljust(8, "\x00")
|
116
255
|
nonce_from_node = [packet_from].pack('V').ljust(8, "\x00")
|
117
256
|
nonce = "#{nonce_packet_id}#{nonce_from_node}".b
|
118
257
|
|
119
|
-
|
120
|
-
|
121
|
-
|
258
|
+
psk = psks[:LongFast]
|
259
|
+
target_chanel = decoded_payload_hash[:channel_id].to_s.to_sym
|
260
|
+
psk = psks[target_chanel] if psks.keys.include?(target_chanel)
|
261
|
+
dec_psk = Base64.strict_decode64(psk)
|
262
|
+
|
263
|
+
cipher = OpenSSL::Cipher.new('AES-128-CTR')
|
264
|
+
cipher = OpenSSL::Cipher.new('AES-256-CTR') if dec_psk.length == 32
|
122
265
|
cipher.decrypt
|
123
266
|
cipher.key = dec_psk
|
124
267
|
cipher.iv = nonce
|
125
268
|
|
126
269
|
decrypted = cipher.update(encrypted_message) + cipher.final
|
127
|
-
message[:
|
270
|
+
message[:decoded] = Meshtastic::Data.decode(decrypted).to_h
|
271
|
+
message[:encrypted] = :decrypted
|
128
272
|
end
|
129
273
|
|
130
274
|
if message[:decoded]
|
275
|
+
# payload = Meshtastic::Data.decode(message[:decoded][:payload]).to_h
|
131
276
|
payload = message[:decoded][:payload]
|
132
|
-
|
133
277
|
msg_type = message[:decoded][:portnum]
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
# when :AUDIO_APP
|
140
|
-
# pb_obj = Meshtastic::Audio.decode(payload)
|
141
|
-
when :DETECTION_SENSOR_APP
|
142
|
-
pb_obj = Meshtastic::DeviceState.decode(payload)
|
143
|
-
# when :IP_TUNNEL_APP
|
144
|
-
# pb_obj = Meshtastic::IpTunnel.decode(payload)
|
145
|
-
when :MAP_REPORT_APP
|
146
|
-
pb_obj = Meshtastic::MapReport.decode(payload)
|
147
|
-
# when :MAX
|
148
|
-
# pb_obj = Meshtastic::Max.decode(payload)
|
149
|
-
when :NEIGHBORINFO_APP
|
150
|
-
pb_obj = Meshtastic::NeighborInfo.decode(payload)
|
151
|
-
when :NODEINFO_APP
|
152
|
-
pb_obj = Meshtastic::User.decode(payload)
|
153
|
-
when :PAXCOUNTER_APP
|
154
|
-
pb_obj = Meshtastic::Paxcount.decode(payload)
|
155
|
-
when :POSITION_APP
|
156
|
-
pb_obj = Meshtastic::Position.decode(payload)
|
157
|
-
# when :PRIVATE_APP
|
158
|
-
# pb_obj = Meshtastic::Private.decode(payload)
|
159
|
-
when :RANGE_TEST_APP
|
160
|
-
# Unsure if this is the correct protobuf object
|
161
|
-
pb_obj = Meshtastic::FromRadio.decode(payload)
|
162
|
-
when :REMOTE_HARDWARE_APP
|
163
|
-
pb_obj = Meshtastic::HardwareMessage.decode(payload)
|
164
|
-
# when :REPLY_APP
|
165
|
-
# pb_obj = Meshtastic::Reply.decode(payload)
|
166
|
-
when :ROUTING_APP
|
167
|
-
pb_obj = Meshtastic::Routing.decode(payload)
|
168
|
-
when :SERIAL_APP
|
169
|
-
pb_obj = Meshtastic::SerialConnectionStatus.decode(payload)
|
170
|
-
when :SIMULATOR_APP,
|
171
|
-
:TEXT_MESSAGE_COMPRESSED_APP
|
172
|
-
# Unsure if this is the correct protobuf object
|
173
|
-
# for TEXT_MESSAGE_COMPRESSED_APP
|
174
|
-
pb_obj = Meshtastic::Compressed.decode(payload)
|
175
|
-
when :STORE_FORWARD_APP
|
176
|
-
pb_obj = Meshtastic::StoreAndForward.decode(payload)
|
177
|
-
when :TEXT_MESSAGE_APP
|
178
|
-
# Unsure if this is the correct protobuf object
|
179
|
-
# pb_obj = Meshtastic::MqttClientProxyMessage.decode(payload)
|
180
|
-
pb_obj = Meshtastic::Data.decode(payload)
|
181
|
-
when :TELEMETRY_APP
|
182
|
-
pb_obj = Meshtastic::Telemetry.decode(payload)
|
183
|
-
when :TRACEROUTE_APP
|
184
|
-
pb_obj = Meshtastic::RouteDiscovery.decode(payload)
|
185
|
-
# when :UNKNOWN_APP
|
186
|
-
# pb_obj = Meshtastic.Unknown.decode(payload)
|
187
|
-
when :WAYPOINT_APP
|
188
|
-
pb_obj = Meshtastic::Waypoint.decode(payload)
|
189
|
-
# when :ZPS_APP
|
190
|
-
# pb_obj = Meshtastic::Zps.decode(payload)
|
191
|
-
else
|
192
|
-
puts "WARNING: Unknown message type: #{msg_type}"
|
193
|
-
end
|
194
|
-
# Overwrite the payload with the decoded protobuf object
|
195
|
-
# message[:decoded][:payload] = pb_obj.to_h unless msg_type == :TRACEROUTE_APP
|
196
|
-
message[:decoded][:payload] = pb_obj.to_h
|
197
|
-
if message[:decoded][:payload].keys.include?(:latitude_i) &&
|
198
|
-
message[:decoded][:payload].keys.include?(:longitude_i) &&
|
199
|
-
gps_metadata
|
200
|
-
|
201
|
-
latitude = pb_obj.to_h[:latitude_i] * 0.0000001
|
202
|
-
longitude = pb_obj.to_h[:longitude_i] * 0.0000001
|
203
|
-
message[:decoded][:payload][:gps_metadata] = gps_search(
|
204
|
-
lat: latitude,
|
205
|
-
lon: longitude
|
206
|
-
).first.data
|
207
|
-
end
|
208
|
-
|
209
|
-
# If we there's a mac address, make it look like one.
|
210
|
-
if message[:decoded][:payload].keys.include?(:macaddr)
|
211
|
-
macaddr = message[:decoded][:payload][:macaddr]
|
212
|
-
macaddr_fmt = macaddr.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join(':')
|
213
|
-
message[:decoded][:payload][:macaddr] = macaddr_fmt
|
214
|
-
end
|
215
|
-
# puts pb_obj.public_methods
|
216
|
-
# message[:decoded][:pb_obj] = pb_obj
|
278
|
+
message[:decoded][:payload] = recursively_decode_payloads(
|
279
|
+
object: payload,
|
280
|
+
msg_type: msg_type,
|
281
|
+
gps_metadata: gps_metadata
|
282
|
+
)
|
217
283
|
end
|
218
284
|
|
219
|
-
|
285
|
+
message[:raw_packet] = raw_packet if include_raw
|
220
286
|
decoded_payload_hash[:packet] = message
|
221
287
|
unless block_given?
|
222
288
|
message[:stdout] = 'pretty'
|
@@ -227,8 +293,11 @@ module Meshtastic
|
|
227
293
|
ArgumentError => e
|
228
294
|
|
229
295
|
message[:decrypted] = e.message if e.message.include?('key must be')
|
296
|
+
message[:decrypted] = 'unable to decrypt - psk?' if e.message.include?('occurred during parsing')
|
230
297
|
decoded_payload_hash[:packet] = message
|
231
298
|
unless block_given?
|
299
|
+
puts "WARNING: #{e.inspect} - MSG IS >>>"
|
300
|
+
# puts e.backtrace
|
232
301
|
message[:stdout] = 'inspect'
|
233
302
|
stdout_message = decoded_payload_hash.inspect
|
234
303
|
end
|
@@ -320,7 +389,7 @@ module Meshtastic
|
|
320
389
|
root_topic: 'optional - root topic (default: msh)',
|
321
390
|
region: 'optional - region e.g. 'US/VA', etc (default: US)',
|
322
391
|
channel: 'optional - channel name e.g. '2/stat/#' (default: '2/e/LongFast/#')',
|
323
|
-
|
392
|
+
psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: 'AQ==' })',
|
324
393
|
qos: 'optional - quality of service (default: 0)',
|
325
394
|
json: 'optional - JSON output (default: false)',
|
326
395
|
filter: 'optional - comma-delimited string(s) to filter on in message (default: nil)',
|
data/lib/meshtastic/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meshtastic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.42
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 0day Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.8.
|
47
|
+
version: 1.8.3
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.8.
|
54
|
+
version: 1.8.3
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: google-protobuf
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,14 +156,14 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - '='
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 2.29.
|
159
|
+
version: 2.29.2
|
160
160
|
type: :runtime
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - '='
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: 2.29.
|
166
|
+
version: 2.29.2
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: rvm
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - '='
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: 1.11.3.9
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: tty-prompt
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - '='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 0.23.1
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - '='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 0.23.1
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
196
|
name: yard
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|