vaultkit 0.1.2 → 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 +4 -4
- data/bin/vkit +1 -1
- data/lib/vkit/cli/base_cli.rb +43 -0
- data/lib/vkit/cli/commands/agent_tokens_create_command.rb +74 -0
- data/lib/vkit/cli/commands/agent_tokens_list_command.rb +68 -0
- data/lib/vkit/cli/commands/agent_tokens_revoke_command.rb +72 -0
- data/lib/vkit/cli/commands/fetch_command.rb +2 -2
- data/lib/vkit/cli/commands/policy_bundle_command.rb +4 -0
- data/lib/vkit/cli.rb +14 -0
- data/lib/vkit/policy/bundle_compiler.rb +4 -0
- data/lib/vkit/policy/policy_validator.rb +129 -0
- data/lib/vkit/policy/validation_error.rb +5 -0
- data/lib/vkit/version.rb +5 -0
- data/lib/vkit.rb +1 -0
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ef641bfa848899716802a548282d99ceb00882f2ebace64e73cad6a465e3f39
|
|
4
|
+
data.tar.gz: b45579cba40972b3500af392d872c226eec3499fa884179fa87dbeb3a0b7711e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ac9dd5b9b100dcdbf1a141f25bf1e686efcf9376dd5182aa3827c0b1457b571e65ba8f5fabe6a17a8473d3e4909306d74ef6003421ddf07f5f77e692848cc7d
|
|
7
|
+
data.tar.gz: f98f55a48d58315877e824663a8c9d4499542f661b838dadf2b5dd534ec66583b43dc4db25d75a55be187aabaa7ae92b2f7abc87634da61ba143f44bfb85d23b
|
data/bin/vkit
CHANGED
data/lib/vkit/cli/base_cli.rb
CHANGED
|
@@ -168,6 +168,49 @@ module Vkit
|
|
|
168
168
|
)
|
|
169
169
|
end
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
desc "agents SUBCOMMAND ...ARGS", "Manage agents and automation identities"
|
|
173
|
+
subcommand "agents", Class.new(Thor) {
|
|
174
|
+
|
|
175
|
+
# agents tokens SUBCOMMAND
|
|
176
|
+
desc "tokens SUBCOMMAND ...ARGS", "Manage agent tokens"
|
|
177
|
+
subcommand "tokens", Class.new(Thor) {
|
|
178
|
+
|
|
179
|
+
# agents tokens list
|
|
180
|
+
desc "list", "List tokens for an agent"
|
|
181
|
+
option :format, type: :string, default: "table", enum: %w[table json]
|
|
182
|
+
def list
|
|
183
|
+
Commands::AgentTokensListCommand.new.call(
|
|
184
|
+
agent: options[:agent],
|
|
185
|
+
format: options[:format]
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# agents tokens create
|
|
190
|
+
desc "create", "Create a new agent token (automation identity)"
|
|
191
|
+
option :name, required: true, desc: "Human-readable name (e.g. billing-bot)"
|
|
192
|
+
option :expires_in, type: :string, desc: "Token lifetime (e.g. 1h, 24h, 30d)"
|
|
193
|
+
option :role, type: :string, default: "agent", desc: "Role assigned to this token"
|
|
194
|
+
def create
|
|
195
|
+
Commands::AgentTokensCreateCommand.new.call(
|
|
196
|
+
name: options[:name],
|
|
197
|
+
expires_in: options[:expires_in],
|
|
198
|
+
role: options[:role]
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# agents tokens revoke
|
|
203
|
+
desc "revoke", "Revoke an agent token"
|
|
204
|
+
option :token, required: true, desc: "Token ID or prefix"
|
|
205
|
+
option :force, type: :boolean, default: false, desc: "Skip confirmation"
|
|
206
|
+
def revoke
|
|
207
|
+
Commands::AgentTokensRevokeCommand.new.call(
|
|
208
|
+
token: options[:token],
|
|
209
|
+
force: options[:force]
|
|
210
|
+
)
|
|
211
|
+
end
|
|
212
|
+
}
|
|
213
|
+
}
|
|
171
214
|
end
|
|
172
215
|
end
|
|
173
216
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
module Vkit
|
|
7
|
+
module CLI
|
|
8
|
+
module Commands
|
|
9
|
+
class AgentTokensCreateCommand < BaseCommand
|
|
10
|
+
def call(options)
|
|
11
|
+
with_auth do
|
|
12
|
+
user = credential_store.user
|
|
13
|
+
org = user["organization_slug"]
|
|
14
|
+
|
|
15
|
+
response =
|
|
16
|
+
authenticated_client.post(
|
|
17
|
+
"/api/v1/orgs/#{org}/agent_tokens",
|
|
18
|
+
body: build_body(options)
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
token = response.fetch("token")
|
|
22
|
+
secret = response.fetch("secret")
|
|
23
|
+
|
|
24
|
+
puts "🔐 AGENT TOKEN ISSUED (shown once)"
|
|
25
|
+
puts
|
|
26
|
+
|
|
27
|
+
puts "🧠 Name: #{token["name"]}"
|
|
28
|
+
puts "🎭 Role: #{token["role"]}"
|
|
29
|
+
puts "🆔 Token ID: #{token["id"]}"
|
|
30
|
+
puts "🏷 Prefix: #{token["prefix"]}"
|
|
31
|
+
puts "⏳ Expires At: #{token["expires_at"] || "never"}"
|
|
32
|
+
puts
|
|
33
|
+
|
|
34
|
+
puts "🔑 TOKEN"
|
|
35
|
+
puts "────────────────────────────────────────"
|
|
36
|
+
puts secret
|
|
37
|
+
puts "────────────────────────────────────────"
|
|
38
|
+
puts
|
|
39
|
+
|
|
40
|
+
puts "⚠️ Store this token securely. It cannot be retrieved again."
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def build_body(options)
|
|
47
|
+
body = {
|
|
48
|
+
name: options.fetch(:name),
|
|
49
|
+
role: options[:role],
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if options[:expires_in]
|
|
53
|
+
body[:expires_at] = parse_expires_in(options[:expires_in]).iso8601
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
body
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def parse_expires_in(value)
|
|
60
|
+
number = value.to_i
|
|
61
|
+
|
|
62
|
+
case value
|
|
63
|
+
when /\A\d+h\z/
|
|
64
|
+
Time.now.utc + (number * 60 * 60)
|
|
65
|
+
when /\A\d+d\z/
|
|
66
|
+
Time.now.utc + (number * 24 * 60 * 60)
|
|
67
|
+
else
|
|
68
|
+
raise "Invalid expires_in format (use 1h, 24h, 30d)"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../../core/table_formatter"
|
|
5
|
+
|
|
6
|
+
module Vkit
|
|
7
|
+
module CLI
|
|
8
|
+
module Commands
|
|
9
|
+
class AgentTokensListCommand < BaseCommand
|
|
10
|
+
def call(options)
|
|
11
|
+
format = options[:format] || "table"
|
|
12
|
+
|
|
13
|
+
with_auth do
|
|
14
|
+
user = credential_store.user
|
|
15
|
+
org = user["organization_slug"]
|
|
16
|
+
|
|
17
|
+
response =
|
|
18
|
+
authenticated_client.get(
|
|
19
|
+
"/api/v1/orgs/#{org}/agent_tokens",
|
|
20
|
+
params: build_query(options)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
tokens = response["tokens"] || []
|
|
24
|
+
|
|
25
|
+
print_result(tokens, format)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def build_query(options)
|
|
32
|
+
{}.tap do |q|
|
|
33
|
+
q[:name] = options[:agent] if options[:agent]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def print_result(tokens, format)
|
|
38
|
+
if tokens.empty?
|
|
39
|
+
puts "No tokens found."
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
case format
|
|
44
|
+
when "json"
|
|
45
|
+
puts JSON.pretty_generate(tokens)
|
|
46
|
+
|
|
47
|
+
when "table"
|
|
48
|
+
Vkit::Core::TableFormatter.render(
|
|
49
|
+
tokens.map do |t|
|
|
50
|
+
{
|
|
51
|
+
"ID" => t["id"],
|
|
52
|
+
"Agent" => t["name"],
|
|
53
|
+
"Prefix" => t["prefix"],
|
|
54
|
+
"Expires At" => t["expires_at"] || "never",
|
|
55
|
+
"Revoked At" => t["revoked_at"] || "-",
|
|
56
|
+
"Created At" => t["created_at"]
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
else
|
|
62
|
+
raise "Unknown format: #{format}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Vkit
|
|
6
|
+
module CLI
|
|
7
|
+
module Commands
|
|
8
|
+
class AgentTokensRevokeCommand < BaseCommand
|
|
9
|
+
def call(options)
|
|
10
|
+
token_ref = options.fetch(:token)
|
|
11
|
+
|
|
12
|
+
with_auth do
|
|
13
|
+
user = credential_store.user
|
|
14
|
+
org = user["organization_slug"]
|
|
15
|
+
|
|
16
|
+
token = resolve_token!(org, token_ref)
|
|
17
|
+
|
|
18
|
+
unless options[:force]
|
|
19
|
+
confirm!(token)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
authenticated_client.post(
|
|
23
|
+
"/api/v1/orgs/#{org}/agent_tokens/#{token["id"]}/revoke",
|
|
24
|
+
body: {}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
puts "🛑 AGENT TOKEN REVOKED"
|
|
28
|
+
puts
|
|
29
|
+
puts "🧠 Name: #{token["name"]}"
|
|
30
|
+
puts "🏷 Prefix: #{token["prefix"]}"
|
|
31
|
+
puts "🆔 ID: #{token["id"]}"
|
|
32
|
+
puts
|
|
33
|
+
puts "✅ The token is no longer valid."
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def resolve_token!(org, token_ref)
|
|
40
|
+
response =
|
|
41
|
+
authenticated_client.get(
|
|
42
|
+
"/api/v1/orgs/#{org}/agent_tokens"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
tokens = response["tokens"] || []
|
|
46
|
+
|
|
47
|
+
token =
|
|
48
|
+
tokens.find do |t|
|
|
49
|
+
t["id"] == token_ref || t["prefix"] == token_ref
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
raise "Agent token not found: #{token_ref}" unless token
|
|
53
|
+
|
|
54
|
+
token
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def confirm!(token)
|
|
58
|
+
puts "⚠️ You are about to revoke this agent token:"
|
|
59
|
+
puts
|
|
60
|
+
puts "🧠 Name: #{token["name"]}"
|
|
61
|
+
puts "🏷 Prefix: #{token["prefix"]}"
|
|
62
|
+
puts "🆔 ID: #{token["id"]}"
|
|
63
|
+
puts
|
|
64
|
+
print "\nType 'revoke' to confirm: "
|
|
65
|
+
|
|
66
|
+
input = STDIN.gets&.strip
|
|
67
|
+
abort "Aborted." unless input == "revoke"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require_relative "../../policy/bundle_compiler"
|
|
6
|
+
require_relative "../../policy/validation_error"
|
|
6
7
|
|
|
7
8
|
module Vkit
|
|
8
9
|
module CLI
|
|
@@ -58,6 +59,9 @@ module Vkit
|
|
|
58
59
|
puts " Checksum: #{bundle.dig("bundle", "checksum")}"
|
|
59
60
|
puts " Output: #{out}"
|
|
60
61
|
end
|
|
62
|
+
rescue Vkit::Policy::ValidationError => e
|
|
63
|
+
puts e.message
|
|
64
|
+
exit 1
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
private
|
data/lib/vkit/cli.rb
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
require "thor"
|
|
2
|
+
require_relative "version"
|
|
2
3
|
|
|
3
4
|
require_relative "cli/commands"
|
|
4
5
|
require_relative "cli/base_cli"
|
|
6
|
+
|
|
7
|
+
module Vkit
|
|
8
|
+
module CLI
|
|
9
|
+
def self.start(argv = ARGV)
|
|
10
|
+
if argv.include?("--version") || argv.include?("-v")
|
|
11
|
+
puts "vkit #{Vkit::VERSION}"
|
|
12
|
+
exit 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
BaseCLI.start(argv)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -2,6 +2,7 @@ require "json"
|
|
|
2
2
|
require "yaml"
|
|
3
3
|
require "digest"
|
|
4
4
|
require "time"
|
|
5
|
+
require_relative "policy_validator"
|
|
5
6
|
|
|
6
7
|
module Vkit
|
|
7
8
|
module Policy
|
|
@@ -40,6 +41,9 @@ module Vkit
|
|
|
40
41
|
files.map do |f|
|
|
41
42
|
data = YAML.load_file(f)
|
|
42
43
|
raise "Policy file #{f} must be a Hash" unless data.is_a?(Hash)
|
|
44
|
+
|
|
45
|
+
PolicyValidator.validate!(data, file: File.basename(f))
|
|
46
|
+
|
|
43
47
|
data.merge("__file" => File.basename(f))
|
|
44
48
|
end
|
|
45
49
|
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module Vkit
|
|
2
|
+
module Policy
|
|
3
|
+
class PolicyValidator
|
|
4
|
+
ACTION_KEYS = %w[deny require_approval mask allow].freeze
|
|
5
|
+
|
|
6
|
+
def self.validate!(policy, file: nil)
|
|
7
|
+
prefix = file ? "In #{file}:\n\n" : ""
|
|
8
|
+
|
|
9
|
+
require_string!(policy, "id", prefix)
|
|
10
|
+
require_string!(policy, "description", prefix)
|
|
11
|
+
|
|
12
|
+
action = policy["action"]
|
|
13
|
+
unless action.is_a?(Hash)
|
|
14
|
+
raise ValidationError, <<~MSG
|
|
15
|
+
#{prefix}Invalid action format.
|
|
16
|
+
|
|
17
|
+
Expected:
|
|
18
|
+
|
|
19
|
+
action:
|
|
20
|
+
deny: true
|
|
21
|
+
|
|
22
|
+
Got:
|
|
23
|
+
|
|
24
|
+
action: #{action.inspect}
|
|
25
|
+
|
|
26
|
+
VaultKit requires `action` to be a mapping so it can attach
|
|
27
|
+
metadata like reason, ttl, approvals, and masking rules.
|
|
28
|
+
MSG
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
validate_action!(action, prefix)
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.require_string!(hash, key, prefix)
|
|
36
|
+
value = hash[key]
|
|
37
|
+
return if value.is_a?(String) && !value.strip.empty?
|
|
38
|
+
|
|
39
|
+
raise ValidationError, <<~MSG
|
|
40
|
+
#{prefix}#{key} is required and must be a non-empty string.
|
|
41
|
+
MSG
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.validate_action!(action, prefix)
|
|
45
|
+
intents = ACTION_KEYS.select { |k| action[k] == true }
|
|
46
|
+
|
|
47
|
+
if intents.empty?
|
|
48
|
+
raise ValidationError, <<~MSG
|
|
49
|
+
#{prefix}Action must specify exactly one intent.
|
|
50
|
+
|
|
51
|
+
Choose one of:
|
|
52
|
+
|
|
53
|
+
- deny
|
|
54
|
+
- require_approval
|
|
55
|
+
- mask
|
|
56
|
+
- allow
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
|
|
60
|
+
action:
|
|
61
|
+
deny: true
|
|
62
|
+
MSG
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if intents.size > 1
|
|
66
|
+
raise ValidationError, <<~MSG
|
|
67
|
+
#{prefix}Action specifies multiple intents: #{intents.join(', ')}.
|
|
68
|
+
|
|
69
|
+
Only one intent may be set to true.
|
|
70
|
+
MSG
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
intent = intents.first
|
|
74
|
+
|
|
75
|
+
case intent
|
|
76
|
+
when "deny"
|
|
77
|
+
require_reason!(action, prefix)
|
|
78
|
+
|
|
79
|
+
when "require_approval"
|
|
80
|
+
require_reason!(action, prefix)
|
|
81
|
+
require_string!(action, "approver_role", prefix)
|
|
82
|
+
|
|
83
|
+
when "mask"
|
|
84
|
+
# masking config can be validated later
|
|
85
|
+
|
|
86
|
+
when "allow"
|
|
87
|
+
# nothing required
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
validate_ttl!(action["ttl"], prefix) if action.key?("ttl")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.require_reason!(action, prefix)
|
|
94
|
+
reason = action["reason"]
|
|
95
|
+
return if reason.is_a?(String) && !reason.strip.empty?
|
|
96
|
+
|
|
97
|
+
raise ValidationError, <<~MSG
|
|
98
|
+
#{prefix}action.reason is required and must be a non-empty string
|
|
99
|
+
when using deny or require_approval.
|
|
100
|
+
MSG
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.validate_ttl!(ttl, prefix)
|
|
104
|
+
case ttl
|
|
105
|
+
when Integer
|
|
106
|
+
return
|
|
107
|
+
when String
|
|
108
|
+
return if ttl.match?(/\A\d+[smhd]\z/)
|
|
109
|
+
|
|
110
|
+
raise ValidationError, <<~MSG
|
|
111
|
+
#{prefix}Invalid ttl format.
|
|
112
|
+
|
|
113
|
+
Expected examples:
|
|
114
|
+
- "30m"
|
|
115
|
+
- "1h"
|
|
116
|
+
- "2d"
|
|
117
|
+
|
|
118
|
+
Got:
|
|
119
|
+
ttl: #{ttl.inspect}
|
|
120
|
+
MSG
|
|
121
|
+
else
|
|
122
|
+
raise ValidationError, <<~MSG
|
|
123
|
+
#{prefix}ttl must be a string duration (e.g. "1h") or an integer (seconds).
|
|
124
|
+
MSG
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
data/lib/vkit/version.rb
ADDED
data/lib/vkit.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vaultkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nnamdi Ogundu
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -40,6 +40,9 @@ files:
|
|
|
40
40
|
- lib/vkit/cli/api/client.rb
|
|
41
41
|
- lib/vkit/cli/base_cli.rb
|
|
42
42
|
- lib/vkit/cli/commands.rb
|
|
43
|
+
- lib/vkit/cli/commands/agent_tokens_create_command.rb
|
|
44
|
+
- lib/vkit/cli/commands/agent_tokens_list_command.rb
|
|
45
|
+
- lib/vkit/cli/commands/agent_tokens_revoke_command.rb
|
|
43
46
|
- lib/vkit/cli/commands/approval_command.rb
|
|
44
47
|
- lib/vkit/cli/commands/base_command.rb
|
|
45
48
|
- lib/vkit/cli/commands/datasource_command.rb
|
|
@@ -61,11 +64,14 @@ files:
|
|
|
61
64
|
- lib/vkit/core/credential_store.rb
|
|
62
65
|
- lib/vkit/core/table_formatter.rb
|
|
63
66
|
- lib/vkit/policy/bundle_compiler.rb
|
|
67
|
+
- lib/vkit/policy/policy_validator.rb
|
|
64
68
|
- lib/vkit/policy/schema/policy_bundle.schema.json
|
|
65
69
|
- lib/vkit/policy/validate_bundle.rb
|
|
70
|
+
- lib/vkit/policy/validation_error.rb
|
|
66
71
|
- lib/vkit/utils/banner.rb
|
|
67
72
|
- lib/vkit/utils/config_loader.rb
|
|
68
73
|
- lib/vkit/utils/logger.rb
|
|
74
|
+
- lib/vkit/version.rb
|
|
69
75
|
homepage: https://vaultkit.io
|
|
70
76
|
licenses:
|
|
71
77
|
- Nonstandard
|