vpn-gateway-tool 1.0.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 +7 -0
- data/README.md +46 -0
- data/bin/vpn-gateway-tool +4 -0
- data/lib/vpn_tool.rb +3 -0
- data/vpn-gateway-tool.gemspec +30 -0
- data/vpn-tool.rb +413 -0
- metadata +46 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1f44715eb6f0c87473d0297f982c74dee1c55cde282d169ff91c49fb98975f59
|
|
4
|
+
data.tar.gz: af89091182c7215e1bc5a39d3e124a63e0e75c6668f44aa54f37346c64b22451
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9ab6d06f6c66f3d2827ebfefe9a9a815a19f9f0b6f7041defaddf74cebd037e53f4388ba22d51419edf5e6ddf03bd992d1b72f59eab6eca58acf6740107074b9
|
|
7
|
+
data.tar.gz: 5cdfd97efbc66dfb67b49e9ab0a8bdbc4b2debf8e401ec5339ab69570b06ba8cbc66e8be9ee7c930145ec180f0804fb359f3f2a93b29a1da09baf5b94f5aab68
|
data/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# vpn-gateway-tool
|
|
2
|
+
|
|
3
|
+
Ruby CLI for the VPN Gateway Manager.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
GET /api/healthz
|
|
9
|
+
GET /api/vpn/countries
|
|
10
|
+
|
|
11
|
+
POST /api/vpn/create?countryCode=
|
|
12
|
+
POST /api/vpn/connect?countryCode=
|
|
13
|
+
POST /api/vpn/disconnect?countryCode=
|
|
14
|
+
|
|
15
|
+
GET /api/vpn/status?countryCode=
|
|
16
|
+
GET /api/vpn/test/sleep?ms=100
|
|
17
|
+
|
|
18
|
+
POST /api/vpn/ip?countryCode=&count=10
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
./vpn-tool.rb --help
|
|
25
|
+
./vpn-tool.rb --version
|
|
26
|
+
./vpn-tool.rb auth register
|
|
27
|
+
./vpn-tool.rb auth login
|
|
28
|
+
./vpn-tool.rb auth status
|
|
29
|
+
./vpn-tool.rb healthz
|
|
30
|
+
./vpn-tool.rb countries
|
|
31
|
+
./vpn-tool.rb create --country MD
|
|
32
|
+
./vpn-tool.rb connect --country MD
|
|
33
|
+
./vpn-tool.rb disconnect --country MD
|
|
34
|
+
./vpn-tool.rb status --country MD
|
|
35
|
+
./vpn-tool.rb test --ms 100
|
|
36
|
+
./vpn-tool.rb ip --country MD --count 10
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## RubyGem
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
gem build vpn-gateway-tool.gemspec
|
|
43
|
+
gem install ./vpn-gateway-tool-1.0.0.gem
|
|
44
|
+
gem signin
|
|
45
|
+
gem push vpn-gateway-tool-1.0.0.gem
|
|
46
|
+
```
|
data/lib/vpn_tool.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/vpn_tool"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "vpn-gateway-tool"
|
|
7
|
+
spec.version = VpnTool::VERSION
|
|
8
|
+
|
|
9
|
+
spec.authors = ["j"]
|
|
10
|
+
spec.email = ["dodi664@gk.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = "VPN Gateway Manager CLI"
|
|
13
|
+
spec.description = "Ruby CLI for a VPN gateway manager."
|
|
14
|
+
spec.homepage = "https://vpn-gateway-manager--jjajajjajahsh.replit.app"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
spec.required_ruby_version = ">= 3.0"
|
|
18
|
+
|
|
19
|
+
spec.files = [
|
|
20
|
+
"vpn-tool.rb",
|
|
21
|
+
"lib/vpn_tool.rb",
|
|
22
|
+
"bin/vpn-gateway-tool",
|
|
23
|
+
"vpn-gateway-tool.gemspec",
|
|
24
|
+
"README.md"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
spec.bindir = "bin"
|
|
28
|
+
spec.executables = ["vpn-gateway-tool"]
|
|
29
|
+
spec.require_paths = ["lib"]
|
|
30
|
+
end
|
data/vpn-tool.rb
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "uri"
|
|
6
|
+
require "json"
|
|
7
|
+
require "openssl"
|
|
8
|
+
require "optparse"
|
|
9
|
+
require "io/console"
|
|
10
|
+
|
|
11
|
+
module VpnTool
|
|
12
|
+
VERSION = "1.0.0"
|
|
13
|
+
API_BASE = "https://vpn-gateway-manager--jjajajjajahsh.replit.app"
|
|
14
|
+
|
|
15
|
+
CONFIG_DIR = File.join(Dir.home, ".vpn-tool")
|
|
16
|
+
AUTH_FILE = File.join(CONFIG_DIR, "auth.json")
|
|
17
|
+
SESSION_FILE = File.join(CONFIG_DIR, "session")
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
def ensure_config
|
|
22
|
+
Dir.mkdir(CONFIG_DIR) unless Dir.exist?(CONFIG_DIR)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def hash_pin(pin)
|
|
26
|
+
OpenSSL::Digest::SHA256.hexdigest(pin)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def read_pin(prompt)
|
|
30
|
+
printf "%s", prompt
|
|
31
|
+
|
|
32
|
+
pin =
|
|
33
|
+
if STDIN.tty?
|
|
34
|
+
STDIN.noecho(&:gets)
|
|
35
|
+
else
|
|
36
|
+
STDIN.gets
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts
|
|
40
|
+
pin.to_s.strip
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def registered?
|
|
44
|
+
File.file?(AUTH_FILE)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def logged_in?
|
|
48
|
+
File.file?(SESSION_FILE) &&
|
|
49
|
+
File.read(SESSION_FILE).strip == "logged_in"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def load_auth
|
|
53
|
+
return nil unless File.file?(AUTH_FILE)
|
|
54
|
+
|
|
55
|
+
JSON.parse(File.read(AUTH_FILE))
|
|
56
|
+
rescue JSON::ParserError
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def save_auth(pin)
|
|
61
|
+
ensure_config
|
|
62
|
+
|
|
63
|
+
File.write(
|
|
64
|
+
AUTH_FILE,
|
|
65
|
+
JSON.pretty_generate(
|
|
66
|
+
{
|
|
67
|
+
"version" => 1,
|
|
68
|
+
"pin_hash" => hash_pin(pin)
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
File.chmod(0o600, AUTH_FILE)
|
|
74
|
+
rescue StandardError
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def require_login
|
|
79
|
+
abort "Not registered. Run: vpn-gateway-tool auth register" unless registered?
|
|
80
|
+
abort "Not logged in. Run: vpn-gateway-tool auth login" unless logged_in?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def auth_help
|
|
84
|
+
puts <<~HELP
|
|
85
|
+
vpn-gateway-tool auth
|
|
86
|
+
|
|
87
|
+
Commands:
|
|
88
|
+
auth register
|
|
89
|
+
auth login
|
|
90
|
+
auth logout
|
|
91
|
+
auth status
|
|
92
|
+
auth reset
|
|
93
|
+
auth remove
|
|
94
|
+
auth help
|
|
95
|
+
HELP
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def auth_register
|
|
99
|
+
abort "PIN already registered." if registered?
|
|
100
|
+
|
|
101
|
+
pin = read_pin("Enter new PIN: ")
|
|
102
|
+
abort "PIN cannot be empty." if pin.empty?
|
|
103
|
+
|
|
104
|
+
confirm = read_pin("Confirm new PIN: ")
|
|
105
|
+
abort "PINs do not match." unless pin == confirm
|
|
106
|
+
|
|
107
|
+
save_auth(pin)
|
|
108
|
+
ensure_config
|
|
109
|
+
File.write(SESSION_FILE, "logged_out")
|
|
110
|
+
|
|
111
|
+
puts "PIN registered successfully."
|
|
112
|
+
puts "Run: vpn-gateway-tool auth login"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def auth_login
|
|
116
|
+
abort "No PIN registered. Run: vpn-gateway-tool auth register" unless registered?
|
|
117
|
+
|
|
118
|
+
auth = load_auth
|
|
119
|
+
pin = read_pin("Enter PIN: ")
|
|
120
|
+
|
|
121
|
+
abort "Invalid PIN." unless auth &&
|
|
122
|
+
auth["pin_hash"] == hash_pin(pin)
|
|
123
|
+
|
|
124
|
+
ensure_config
|
|
125
|
+
File.write(SESSION_FILE, "logged_in")
|
|
126
|
+
|
|
127
|
+
puts "Login successful."
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def auth_logout
|
|
131
|
+
abort "No PIN registered." unless registered?
|
|
132
|
+
|
|
133
|
+
auth = load_auth
|
|
134
|
+
pin = read_pin("Enter PIN: ")
|
|
135
|
+
|
|
136
|
+
abort "Invalid PIN." unless auth &&
|
|
137
|
+
auth["pin_hash"] == hash_pin(pin)
|
|
138
|
+
|
|
139
|
+
ensure_config
|
|
140
|
+
File.write(SESSION_FILE, "logged_out")
|
|
141
|
+
|
|
142
|
+
puts "Logout successful."
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def auth_status
|
|
146
|
+
puts "Registered: #{registered? ? 'yes' : 'no'}"
|
|
147
|
+
puts "Logged in: #{logged_in? ? 'yes' : 'no'}"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def auth_reset
|
|
151
|
+
abort "No PIN registered." unless registered?
|
|
152
|
+
|
|
153
|
+
auth = load_auth
|
|
154
|
+
current = read_pin("Enter current PIN: ")
|
|
155
|
+
|
|
156
|
+
abort "Invalid current PIN." unless auth &&
|
|
157
|
+
auth["pin_hash"] == hash_pin(current)
|
|
158
|
+
|
|
159
|
+
new_pin = read_pin("Enter new PIN: ")
|
|
160
|
+
abort "New PIN cannot be empty." if new_pin.empty?
|
|
161
|
+
|
|
162
|
+
confirm = read_pin("Confirm new PIN: ")
|
|
163
|
+
abort "PINs do not match." unless new_pin == confirm
|
|
164
|
+
|
|
165
|
+
save_auth(new_pin)
|
|
166
|
+
ensure_config
|
|
167
|
+
File.write(SESSION_FILE, "logged_out")
|
|
168
|
+
|
|
169
|
+
puts "PIN reset successfully."
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def auth_remove
|
|
173
|
+
abort "No PIN registered." unless registered?
|
|
174
|
+
|
|
175
|
+
auth = load_auth
|
|
176
|
+
pin = read_pin("Enter PIN: ")
|
|
177
|
+
|
|
178
|
+
abort "Invalid PIN." unless auth &&
|
|
179
|
+
auth["pin_hash"] == hash_pin(pin)
|
|
180
|
+
|
|
181
|
+
File.delete(AUTH_FILE) if File.file?(AUTH_FILE)
|
|
182
|
+
File.delete(SESSION_FILE) if File.file?(SESSION_FILE)
|
|
183
|
+
|
|
184
|
+
puts "Authentication removed."
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def api_request(method, path)
|
|
188
|
+
uri = URI.join(API_BASE, path)
|
|
189
|
+
|
|
190
|
+
request =
|
|
191
|
+
case method
|
|
192
|
+
when :get
|
|
193
|
+
Net::HTTP::Get.new(uri)
|
|
194
|
+
when :post
|
|
195
|
+
Net::HTTP::Post.new(uri)
|
|
196
|
+
else
|
|
197
|
+
raise "Unsupported HTTP method"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
request["Accept"] = "application/json"
|
|
201
|
+
|
|
202
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
203
|
+
http.use_ssl = uri.scheme == "https"
|
|
204
|
+
http.open_timeout = 15
|
|
205
|
+
http.read_timeout = 60
|
|
206
|
+
|
|
207
|
+
response = http.request(request)
|
|
208
|
+
|
|
209
|
+
body =
|
|
210
|
+
begin
|
|
211
|
+
JSON.parse(response.body)
|
|
212
|
+
rescue JSON::ParserError
|
|
213
|
+
response.body
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
{
|
|
217
|
+
"status" => response.code.to_i,
|
|
218
|
+
"body" => body
|
|
219
|
+
}
|
|
220
|
+
rescue StandardError => e
|
|
221
|
+
{
|
|
222
|
+
"status" => 0,
|
|
223
|
+
"body" => {
|
|
224
|
+
"error" => e.message
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def print_response(result)
|
|
230
|
+
puts JSON.pretty_generate(result)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def country_option(args)
|
|
234
|
+
options = {}
|
|
235
|
+
|
|
236
|
+
parser = OptionParser.new do |opts|
|
|
237
|
+
opts.banner = "Usage: vpn-gateway-tool COMMAND --country CODE"
|
|
238
|
+
|
|
239
|
+
opts.on("--country CODE", "Country code") do |value|
|
|
240
|
+
options[:country] = value
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
parser.parse!(args)
|
|
245
|
+
|
|
246
|
+
abort "Missing --country CODE" unless options[:country]
|
|
247
|
+
|
|
248
|
+
options[:country]
|
|
249
|
+
rescue OptionParser::ParseError => e
|
|
250
|
+
abort e.message
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def healthz
|
|
254
|
+
require_login
|
|
255
|
+
print_response(api_request(:get, "/api/healthz"))
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def countries
|
|
259
|
+
require_login
|
|
260
|
+
print_response(api_request(:get, "/api/vpn/countries"))
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def vpn_command(command, args)
|
|
264
|
+
require_login
|
|
265
|
+
|
|
266
|
+
country = country_option(args)
|
|
267
|
+
country = URI.encode_www_form_component(country)
|
|
268
|
+
|
|
269
|
+
method = command == "status" ? :get : :post
|
|
270
|
+
|
|
271
|
+
print_response(
|
|
272
|
+
api_request(
|
|
273
|
+
method,
|
|
274
|
+
"/api/vpn/#{command}?countryCode=#{country}"
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def test_command(args)
|
|
280
|
+
require_login
|
|
281
|
+
|
|
282
|
+
options = { ms: 100 }
|
|
283
|
+
|
|
284
|
+
parser = OptionParser.new do |opts|
|
|
285
|
+
opts.banner = "Usage: vpn-gateway-tool test --ms N"
|
|
286
|
+
|
|
287
|
+
opts.on("--ms N", Integer, "Sleep milliseconds") do |value|
|
|
288
|
+
options[:ms] = value
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
parser.parse!(args)
|
|
293
|
+
|
|
294
|
+
print_response(
|
|
295
|
+
api_request(
|
|
296
|
+
:get,
|
|
297
|
+
"/api/vpn/test/sleep?ms=#{options[:ms]}"
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
rescue OptionParser::ParseError => e
|
|
301
|
+
abort e.message
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def ip_command(args)
|
|
305
|
+
require_login
|
|
306
|
+
|
|
307
|
+
options = {
|
|
308
|
+
country: nil,
|
|
309
|
+
count: 10
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
parser = OptionParser.new do |opts|
|
|
313
|
+
opts.banner = "Usage: vpn-gateway-tool ip --country CODE --count N"
|
|
314
|
+
|
|
315
|
+
opts.on("--country CODE", "Country code") do |value|
|
|
316
|
+
options[:country] = value
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
opts.on("--count N", Integer, "Number of IPs") do |value|
|
|
320
|
+
options[:count] = value
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
parser.parse!(args)
|
|
325
|
+
|
|
326
|
+
abort "Missing --country CODE" unless options[:country]
|
|
327
|
+
|
|
328
|
+
country = URI.encode_www_form_component(options[:country])
|
|
329
|
+
|
|
330
|
+
print_response(
|
|
331
|
+
api_request(
|
|
332
|
+
:post,
|
|
333
|
+
"/api/vpn/ip?countryCode=#{country}&count=#{options[:count]}"
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
rescue OptionParser::ParseError => e
|
|
337
|
+
abort e.message
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def help
|
|
341
|
+
puts <<~HELP
|
|
342
|
+
vpn-gateway-tool #{VERSION}
|
|
343
|
+
|
|
344
|
+
Usage:
|
|
345
|
+
vpn-gateway-tool COMMAND
|
|
346
|
+
|
|
347
|
+
Authentication:
|
|
348
|
+
auth register
|
|
349
|
+
auth login
|
|
350
|
+
auth logout
|
|
351
|
+
auth status
|
|
352
|
+
auth reset
|
|
353
|
+
auth remove
|
|
354
|
+
|
|
355
|
+
VPN:
|
|
356
|
+
healthz
|
|
357
|
+
countries
|
|
358
|
+
create --country CODE
|
|
359
|
+
connect --country CODE
|
|
360
|
+
disconnect --country CODE
|
|
361
|
+
status --country CODE
|
|
362
|
+
test --ms N
|
|
363
|
+
ip --country CODE --count N
|
|
364
|
+
HELP
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def run(argv = ARGV)
|
|
368
|
+
parser = OptionParser.new do |opts|
|
|
369
|
+
opts.on("--version") do
|
|
370
|
+
puts "vpn-gateway-tool #{VERSION}"
|
|
371
|
+
exit
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
opts.on("-h", "--help") do
|
|
375
|
+
help
|
|
376
|
+
exit
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
parser.parse!(argv)
|
|
381
|
+
|
|
382
|
+
command = argv.shift
|
|
383
|
+
|
|
384
|
+
case command
|
|
385
|
+
when "auth"
|
|
386
|
+
case argv.shift
|
|
387
|
+
when "register" then auth_register
|
|
388
|
+
when "login" then auth_login
|
|
389
|
+
when "logout" then auth_logout
|
|
390
|
+
when "status" then auth_status
|
|
391
|
+
when "reset" then auth_reset
|
|
392
|
+
when "remove" then auth_remove
|
|
393
|
+
else auth_help
|
|
394
|
+
end
|
|
395
|
+
when "healthz"
|
|
396
|
+
healthz
|
|
397
|
+
when "countries"
|
|
398
|
+
countries
|
|
399
|
+
when "create", "connect", "disconnect", "status"
|
|
400
|
+
vpn_command(command, argv)
|
|
401
|
+
when "test"
|
|
402
|
+
test_command(argv)
|
|
403
|
+
when "ip"
|
|
404
|
+
ip_command(argv)
|
|
405
|
+
when "version"
|
|
406
|
+
puts "vpn-gateway-tool #{VERSION}"
|
|
407
|
+
else
|
|
408
|
+
help
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
VpnTool.run
|
metadata
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: vpn-gateway-tool
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- j
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-08-17 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Ruby CLI for a VPN gateway manager.
|
|
13
|
+
email:
|
|
14
|
+
- dodi664@gk.com
|
|
15
|
+
executables:
|
|
16
|
+
- vpn-gateway-tool
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- README.md
|
|
21
|
+
- bin/vpn-gateway-tool
|
|
22
|
+
- lib/vpn_tool.rb
|
|
23
|
+
- vpn-gateway-tool.gemspec
|
|
24
|
+
- vpn-tool.rb
|
|
25
|
+
homepage: https://vpn-gateway-manager--jjajajjajahsh.replit.app
|
|
26
|
+
licenses:
|
|
27
|
+
- MIT
|
|
28
|
+
metadata: {}
|
|
29
|
+
rdoc_options: []
|
|
30
|
+
require_paths:
|
|
31
|
+
- lib
|
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
33
|
+
requirements:
|
|
34
|
+
- - ">="
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '3.0'
|
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0'
|
|
42
|
+
requirements: []
|
|
43
|
+
rubygems_version: 3.6.2
|
|
44
|
+
specification_version: 4
|
|
45
|
+
summary: VPN Gateway Manager CLI
|
|
46
|
+
test_files: []
|