pgai 1.0.5 → 1.1.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/README.md +9 -0
- data/lib/pgai/cli/enc.rb +53 -0
- data/lib/pgai/cli/main.rb +3 -0
- data/lib/pgai/client.rb +0 -2
- data/lib/pgai/commander.rb +1 -5
- data/lib/pgai/create_clone_service.rb +0 -2
- data/lib/pgai/encryption/key.rb +40 -0
- data/lib/pgai/encryption/migrator.rb +73 -0
- data/lib/pgai/encryption/one_password_client.rb +58 -0
- data/lib/pgai/encryption/prompter.rb +32 -0
- data/lib/pgai/encryption/reference_store.rb +35 -0
- data/lib/pgai/encryption/store.rb +26 -0
- data/lib/pgai/port/allocator.rb +0 -2
- data/lib/pgai/port/forwarder.rb +0 -3
- data/lib/pgai/port/manager.rb +0 -2
- data/lib/pgai/resources/attributes.rb +0 -2
- data/lib/pgai/resources/local/base_record.rb +0 -2
- data/lib/pgai/resources/remote/db_object.rb +0 -2
- data/lib/pgai/store.rb +33 -21
- data/lib/pgai/version.rb +1 -1
- data/lib/pgai.rb +27 -2
- metadata +37 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: decf99fdd8015349a1f8ee139ac98a2e1abcbf2ea8c34c286158c72bb4cb9832
|
|
4
|
+
data.tar.gz: 3a4e095f9edb679451766b4ce0cb7f33be94855f0ea680c93782b8c4668168ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e9148734a065833aa71abc9f51c477339d442e3669021fd97aed5bf83ea590a49be7a88a4270944540921b7be1d898c6b85bbc5feb3f44cbdda78c682e5a7572
|
|
7
|
+
data.tar.gz: aaa8d36528923b16bf13156be9f87056d102ae4616fd60f939bb8143363ea8b4f010646ab66abdedcb6db946a5faa309f802e7a7aa8990081d394fef1f97e0e5
|
data/README.md
CHANGED
|
@@ -80,6 +80,15 @@ pgai use -o ci -v -- bin/rails c -e test
|
|
|
80
80
|
- The environment ID serves as the proxy host and the database name can be configured per environment.
|
|
81
81
|
- Environments can share the same remote port value.
|
|
82
82
|
|
|
83
|
+
## Upgrading to 1.1.0 and above
|
|
84
|
+
|
|
85
|
+
pgai now integrates with 1password to encrypt the token and clone details. Run these commands to migrate to the encrypted store:
|
|
86
|
+
|
|
87
|
+
```shell
|
|
88
|
+
pgai enc keygen
|
|
89
|
+
pgai enc migrate
|
|
90
|
+
```
|
|
91
|
+
|
|
83
92
|
## Contributing
|
|
84
93
|
|
|
85
94
|
Bug reports and pull requests are welcome on GitLab at https://gitlab.com/mbobin/pgai.
|
data/lib/pgai/cli/enc.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Pgai::Cli
|
|
2
|
+
class Enc < Base
|
|
3
|
+
desc "keygen", "Generate a new encryption key and save it to 1Password"
|
|
4
|
+
def keygen
|
|
5
|
+
encryption_key = build_encryption_key
|
|
6
|
+
key = encryption_key.generate
|
|
7
|
+
|
|
8
|
+
say "Generated new encryption key", :green
|
|
9
|
+
say ""
|
|
10
|
+
|
|
11
|
+
reference = encryption_key.save(key)
|
|
12
|
+
|
|
13
|
+
say ""
|
|
14
|
+
say "✓ Key saved to 1Password", :green
|
|
15
|
+
say " Reference: #{reference}", :cyan
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc "migrate", "Migrate from unencrypted to encrypted store"
|
|
19
|
+
def migrate
|
|
20
|
+
migrator = build_migrator
|
|
21
|
+
|
|
22
|
+
unless migrator.migration_needed?
|
|
23
|
+
say "No migration needed. Already using encrypted store.", :green
|
|
24
|
+
return
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
say "Starting migration from unencrypted to encrypted store...", :cyan
|
|
28
|
+
|
|
29
|
+
result = migrator.migrate
|
|
30
|
+
|
|
31
|
+
say ""
|
|
32
|
+
say "✓ Migration complete!", :green
|
|
33
|
+
say " Migrated #{result[:count]} record types", :cyan
|
|
34
|
+
say " Legacy store backed up to: #{result[:backup_path]}", :cyan
|
|
35
|
+
say " Remove it if the migration was successful", :cyan
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def build_encryption_key
|
|
41
|
+
Pgai::Encryption::Key.new(
|
|
42
|
+
prompter: Pgai::Encryption::Prompter.new(shell: self)
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def build_migrator
|
|
47
|
+
Pgai::Encryption::Migrator.new(
|
|
48
|
+
key: Pgai::Encryption::Key.new.read,
|
|
49
|
+
config_dir: Pgai.config_dir
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/pgai/cli/main.rb
CHANGED
data/lib/pgai/client.rb
CHANGED
data/lib/pgai/commander.rb
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "tty-logger"
|
|
4
|
-
require "singleton"
|
|
5
|
-
require "forwardable"
|
|
6
|
-
|
|
7
3
|
module Pgai
|
|
8
4
|
class Commander
|
|
9
5
|
include Singleton
|
|
@@ -29,7 +25,7 @@ module Pgai
|
|
|
29
25
|
end
|
|
30
26
|
|
|
31
27
|
def store
|
|
32
|
-
@store ||= Pgai::Store.new
|
|
28
|
+
@store ||= Pgai::Store.new(key: Pgai::Encryption::Key.new.read)
|
|
33
29
|
end
|
|
34
30
|
|
|
35
31
|
def config
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgai
|
|
4
|
+
module Encryption
|
|
5
|
+
class Key
|
|
6
|
+
ENV_VAR_NAME = "PGAI_MASTER_KEY"
|
|
7
|
+
|
|
8
|
+
def initialize(op_client: OnePasswordClient.new, reference_store: ReferenceStore.new, prompter: Prompter.new)
|
|
9
|
+
@op_client = op_client
|
|
10
|
+
@reference_store = reference_store
|
|
11
|
+
@prompter = prompter
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def generate
|
|
15
|
+
Lockbox.generate_key
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def save(key)
|
|
19
|
+
config = @prompter.prompt_for_config
|
|
20
|
+
reference = @op_client.create_item(key: key, **config)
|
|
21
|
+
|
|
22
|
+
@reference_store.write(reference)
|
|
23
|
+
reference
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def read
|
|
27
|
+
ENV.fetch(ENV_VAR_NAME) { read_from_one_password }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def read_from_one_password
|
|
33
|
+
reference = @reference_store.read
|
|
34
|
+
return nil unless reference
|
|
35
|
+
|
|
36
|
+
@op_client.read_item(reference)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgai
|
|
4
|
+
module Encryption
|
|
5
|
+
class Migrator
|
|
6
|
+
def initialize(key:, config_dir: Pgai.config_dir)
|
|
7
|
+
@key = key
|
|
8
|
+
@config_dir = config_dir
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def migration_needed?
|
|
12
|
+
legacy_store_path.exist? && !encrypted_store_path.exist?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def migrate
|
|
16
|
+
validate_prerequisites!
|
|
17
|
+
|
|
18
|
+
record_types = read_legacy_data
|
|
19
|
+
write_encrypted_data(record_types)
|
|
20
|
+
backup_legacy_store
|
|
21
|
+
|
|
22
|
+
{count: record_types.size, backup_path: backup_path}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def validate_prerequisites!
|
|
28
|
+
raise Pgai::CliError, "Encryption key not found. Run 'pgai enc keygen' first." unless @key
|
|
29
|
+
raise Pgai::CliError, "Legacy store not found at #{legacy_store_path}" unless legacy_store_path.exist?
|
|
30
|
+
raise Pgai::CliError, "Encrypted store already exists at #{encrypted_store_path}" if encrypted_store_path.exist?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def read_legacy_data
|
|
34
|
+
legacy_store = PStore.new(legacy_store_path)
|
|
35
|
+
record_types = {}
|
|
36
|
+
|
|
37
|
+
legacy_store.transaction(true) do
|
|
38
|
+
legacy_store.roots.each do |root|
|
|
39
|
+
record_types[root] = legacy_store[root]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
record_types
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def write_encrypted_data(record_types)
|
|
47
|
+
encrypted_store = Encryption::Store.new(encrypted_store_path, key: @key)
|
|
48
|
+
|
|
49
|
+
encrypted_store.transaction do
|
|
50
|
+
record_types.each do |type, data|
|
|
51
|
+
encrypted_store[type] = data
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def backup_legacy_store
|
|
57
|
+
FileUtils.mv(legacy_store_path, backup_path)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def legacy_store_path
|
|
61
|
+
@legacy_store_path ||= @config_dir.join(Pgai::Store::LEGACY_STORE_NAME)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def encrypted_store_path
|
|
65
|
+
@encrypted_store_path ||= @config_dir.join(Pgai::Store::STORE_NAME)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def backup_path
|
|
69
|
+
@backup_path ||= @config_dir.join("#{Pgai::Store::LEGACY_STORE_NAME}.backup")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgai
|
|
4
|
+
module Encryption
|
|
5
|
+
class OnePasswordClient
|
|
6
|
+
class CommandError < StandardError; end
|
|
7
|
+
|
|
8
|
+
FIELD_NAME = "master_key"
|
|
9
|
+
|
|
10
|
+
def create_item(key:, vault:, title:, category:)
|
|
11
|
+
item_json = JSON.generate({
|
|
12
|
+
title: title,
|
|
13
|
+
fields: [
|
|
14
|
+
{
|
|
15
|
+
id: FIELD_NAME,
|
|
16
|
+
type: "CONCEALED",
|
|
17
|
+
label: FIELD_NAME,
|
|
18
|
+
value: key
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
output = run_command(
|
|
24
|
+
"op", "item", "create",
|
|
25
|
+
"--vault", vault,
|
|
26
|
+
"--category", category,
|
|
27
|
+
"--format", "json",
|
|
28
|
+
"-",
|
|
29
|
+
stdin_data: item_json
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
build_reference_from(output)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def read_item(reference)
|
|
36
|
+
run_command("op", "read", reference).strip
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def run_command(*args, stdin_data: "")
|
|
42
|
+
stdout, stderr, status = Open3.capture3(*args, stdin_data: stdin_data)
|
|
43
|
+
|
|
44
|
+
raise CommandError, "1Password CLI error: #{stderr}" unless status.success?
|
|
45
|
+
|
|
46
|
+
stdout
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def build_reference_from(json_output)
|
|
50
|
+
item = JSON.parse(json_output)
|
|
51
|
+
vault_name = item.dig("vault", "name")
|
|
52
|
+
item_id = item.fetch("id")
|
|
53
|
+
|
|
54
|
+
"op://#{vault_name}/#{item_id}/#{FIELD_NAME}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgai
|
|
4
|
+
module Encryption
|
|
5
|
+
class Prompter
|
|
6
|
+
DEFAULTS = {
|
|
7
|
+
vault: "employee",
|
|
8
|
+
category: "API Credential",
|
|
9
|
+
title: "pgai master key"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def initialize(shell: Thor::Shell::Basic.new)
|
|
13
|
+
@shell = shell
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def prompt_for_config
|
|
17
|
+
{
|
|
18
|
+
vault: ask_with_default("Vault", DEFAULTS[:vault]),
|
|
19
|
+
category: ask_with_default("Category", DEFAULTS[:category]),
|
|
20
|
+
title: ask_with_default("Title", DEFAULTS[:title])
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def ask_with_default(prompt, default)
|
|
27
|
+
response = @shell.ask("#{prompt} [#{default}]:")
|
|
28
|
+
response.to_s.strip.empty? ? default : response
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgai
|
|
4
|
+
module Encryption
|
|
5
|
+
class ReferenceStore
|
|
6
|
+
def initialize(path: nil)
|
|
7
|
+
path ||= Pgai.config_dir.join("op_ref")
|
|
8
|
+
@path = File.expand_path(path)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def write(reference)
|
|
12
|
+
ensure_directory_exists
|
|
13
|
+
File.write(@path, reference)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def read
|
|
17
|
+
return nil unless File.exist?(@path)
|
|
18
|
+
|
|
19
|
+
File.read(@path).strip
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def exists?
|
|
23
|
+
File.exist?(@path) && !read.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :path
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def ensure_directory_exists
|
|
31
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgai
|
|
4
|
+
module Encryption
|
|
5
|
+
class Store < PStore
|
|
6
|
+
def initialize(file_path, key:, thread_safe: false)
|
|
7
|
+
@lockbox = Lockbox.new(key: key, encode: true)
|
|
8
|
+
super(file_path, thread_safe)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def dump(table)
|
|
14
|
+
data = Marshal.dump(table)
|
|
15
|
+
@lockbox.encrypt(data)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def load(content)
|
|
19
|
+
content = content.read if content.is_a?(File)
|
|
20
|
+
decrypted = @lockbox.decrypt(content)
|
|
21
|
+
|
|
22
|
+
Marshal.load(decrypted)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/pgai/port/allocator.rb
CHANGED
data/lib/pgai/port/forwarder.rb
CHANGED
data/lib/pgai/port/manager.rb
CHANGED
data/lib/pgai/store.rb
CHANGED
|
@@ -1,49 +1,61 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "pathname"
|
|
4
|
-
require "pstore"
|
|
5
|
-
require "fileutils"
|
|
6
|
-
|
|
7
3
|
module Pgai
|
|
8
4
|
class Store
|
|
9
|
-
|
|
5
|
+
STORE_NAME = "config.enc.pstore"
|
|
6
|
+
LEGACY_STORE_NAME = "config.pstore"
|
|
7
|
+
|
|
8
|
+
def initialize(key: nil, config_dir: Pgai.config_dir)
|
|
9
|
+
@key = key
|
|
10
|
+
@config_dir = config_dir
|
|
11
|
+
end
|
|
10
12
|
|
|
11
13
|
def all(record_type)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
backend.transaction(true) do
|
|
15
|
+
backend[record_type]&.values || {}
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def find(record_type, id)
|
|
18
|
-
|
|
19
|
-
(
|
|
20
|
+
backend.transaction(true) do
|
|
21
|
+
(backend[record_type] || {})[id]
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def delete(record_type, id)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
backend.transaction do
|
|
27
|
+
backend[record_type] ||= {}
|
|
28
|
+
backend[record_type] = backend[record_type].except(id)
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def save(record_type, attributes, key: :id)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
backend.transaction do
|
|
34
|
+
backend[record_type] ||= {}
|
|
35
|
+
backend[record_type].merge!(attributes[key] => attributes)
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
|
|
39
|
+
def encrypted?
|
|
40
|
+
backend.is_a?(Encryption::Store)
|
|
41
|
+
end
|
|
42
|
+
|
|
37
43
|
private
|
|
38
44
|
|
|
39
|
-
def
|
|
40
|
-
@
|
|
45
|
+
def backend
|
|
46
|
+
@backend ||= if pstore_path.exist? || !legacy_pstore_path.exist?
|
|
47
|
+
Encryption::Store.new(pstore_path, key: @key)
|
|
48
|
+
else
|
|
49
|
+
PStore.new(legacy_pstore_path)
|
|
50
|
+
end
|
|
41
51
|
end
|
|
42
52
|
|
|
43
|
-
def
|
|
44
|
-
@
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
def pstore_path
|
|
54
|
+
@pstore_path ||= @config_dir.join(STORE_NAME)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def legacy_pstore_path
|
|
58
|
+
@legacy_pstore_path ||= @config_dir.join(LEGACY_STORE_NAME)
|
|
47
59
|
end
|
|
48
60
|
end
|
|
49
61
|
end
|
data/lib/pgai/version.rb
CHANGED
data/lib/pgai.rb
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "date"
|
|
6
|
+
require "securerandom"
|
|
7
|
+
require "forwardable"
|
|
8
|
+
require "singleton"
|
|
9
|
+
require "json"
|
|
10
|
+
require "pstore"
|
|
11
|
+
require "lockbox"
|
|
12
|
+
require "thor"
|
|
13
|
+
require "open3"
|
|
14
|
+
require "socket"
|
|
15
|
+
require "net/ssh"
|
|
16
|
+
require "concurrent-ruby"
|
|
17
|
+
require "excon"
|
|
18
|
+
require "tty-logger"
|
|
19
|
+
require "tty-progressbar"
|
|
3
20
|
require "zeitwerk"
|
|
4
21
|
|
|
5
22
|
loader = Zeitwerk::Loader.for_gem
|
|
6
23
|
loader.setup
|
|
7
24
|
|
|
8
|
-
require "thor"
|
|
9
|
-
|
|
10
25
|
module Pgai
|
|
11
26
|
class Error < StandardError; end
|
|
12
27
|
|
|
@@ -17,4 +32,14 @@ module Pgai
|
|
|
17
32
|
class UnauthorizedError < CliError; end
|
|
18
33
|
|
|
19
34
|
class BadRequestError < CliError; end
|
|
35
|
+
|
|
36
|
+
CONFIG_DIR = "~/.config/pgai/"
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
attr_writer :config_dir
|
|
40
|
+
|
|
41
|
+
def config_dir
|
|
42
|
+
@config_dir ||= Pathname(CONFIG_DIR).expand_path
|
|
43
|
+
end
|
|
44
|
+
end
|
|
20
45
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pgai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marius Bobin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -172,6 +172,34 @@ dependencies:
|
|
|
172
172
|
- - ">="
|
|
173
173
|
- !ruby/object:Gem::Version
|
|
174
174
|
version: 1.2.3
|
|
175
|
+
- !ruby/object:Gem::Dependency
|
|
176
|
+
name: lockbox
|
|
177
|
+
requirement: !ruby/object:Gem::Requirement
|
|
178
|
+
requirements:
|
|
179
|
+
- - "~>"
|
|
180
|
+
- !ruby/object:Gem::Version
|
|
181
|
+
version: '2.1'
|
|
182
|
+
type: :runtime
|
|
183
|
+
prerelease: false
|
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
185
|
+
requirements:
|
|
186
|
+
- - "~>"
|
|
187
|
+
- !ruby/object:Gem::Version
|
|
188
|
+
version: '2.1'
|
|
189
|
+
- !ruby/object:Gem::Dependency
|
|
190
|
+
name: base64
|
|
191
|
+
requirement: !ruby/object:Gem::Requirement
|
|
192
|
+
requirements:
|
|
193
|
+
- - "~>"
|
|
194
|
+
- !ruby/object:Gem::Version
|
|
195
|
+
version: 0.3.0
|
|
196
|
+
type: :runtime
|
|
197
|
+
prerelease: false
|
|
198
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
199
|
+
requirements:
|
|
200
|
+
- - "~>"
|
|
201
|
+
- !ruby/object:Gem::Version
|
|
202
|
+
version: 0.3.0
|
|
175
203
|
- !ruby/object:Gem::Dependency
|
|
176
204
|
name: rake
|
|
177
205
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -241,12 +269,19 @@ files:
|
|
|
241
269
|
- bin/pgai
|
|
242
270
|
- lib/pgai.rb
|
|
243
271
|
- lib/pgai/cli/base.rb
|
|
272
|
+
- lib/pgai/cli/enc.rb
|
|
244
273
|
- lib/pgai/cli/env.rb
|
|
245
274
|
- lib/pgai/cli/main.rb
|
|
246
275
|
- lib/pgai/client.rb
|
|
247
276
|
- lib/pgai/clone_manager.rb
|
|
248
277
|
- lib/pgai/commander.rb
|
|
249
278
|
- lib/pgai/create_clone_service.rb
|
|
279
|
+
- lib/pgai/encryption/key.rb
|
|
280
|
+
- lib/pgai/encryption/migrator.rb
|
|
281
|
+
- lib/pgai/encryption/one_password_client.rb
|
|
282
|
+
- lib/pgai/encryption/prompter.rb
|
|
283
|
+
- lib/pgai/encryption/reference_store.rb
|
|
284
|
+
- lib/pgai/encryption/store.rb
|
|
250
285
|
- lib/pgai/external_command_manager.rb
|
|
251
286
|
- lib/pgai/port/allocator.rb
|
|
252
287
|
- lib/pgai/port/forwarder.rb
|