ruby_llm-docker 0.2.5 → 0.3.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/examples/docker_chat.rb +41 -0
- data/lib/ruby_llm/docker/build_image.rb +64 -65
- data/lib/ruby_llm/docker/copy_to_container.rb +89 -134
- data/lib/ruby_llm/docker/create_container.rb +64 -86
- data/lib/ruby_llm/docker/create_network.rb +63 -79
- data/lib/ruby_llm/docker/create_volume.rb +57 -79
- data/lib/ruby_llm/docker/exec_container.rb +94 -111
- data/lib/ruby_llm/docker/fetch_container_logs.rb +63 -85
- data/lib/ruby_llm/docker/list_containers.rb +46 -40
- data/lib/ruby_llm/docker/list_images.rb +34 -55
- data/lib/ruby_llm/docker/list_networks.rb +35 -57
- data/lib/ruby_llm/docker/list_volumes.rb +35 -64
- data/lib/ruby_llm/docker/pull_image.rb +24 -48
- data/lib/ruby_llm/docker/push_image.rb +60 -89
- data/lib/ruby_llm/docker/recreate_container.rb +47 -79
- data/lib/ruby_llm/docker/remove_container.rb +48 -76
- data/lib/ruby_llm/docker/remove_image.rb +53 -95
- data/lib/ruby_llm/docker/remove_network.rb +42 -78
- data/lib/ruby_llm/docker/remove_volume.rb +52 -85
- data/lib/ruby_llm/docker/run_container.rb +73 -53
- data/lib/ruby_llm/docker/start_container.rb +35 -58
- data/lib/ruby_llm/docker/stop_container.rb +40 -66
- data/lib/ruby_llm/docker/tag_image.rb +67 -95
- data/lib/ruby_llm/docker/version.rb +10 -1
- data/lib/ruby_llm/docker.rb +13 -0
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 17035d50b89aaa2e3cfd6409b1a827a3dc9d6e8cc6013e703105b1bce1d6a80e
|
|
4
|
+
data.tar.gz: aac7610a23113ca2b1cad98e1d7eaa88210d069cf7cf9eaedf28736c526b2599
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 152b3442d3130660f7aad1815a7d16918fe20c61a06579f7ab4815a97af25c49cb581c39103b5b08bcf352ad0052cd8e9c601b9e320e2a0606ba3a7ee5b096fa
|
|
7
|
+
data.tar.gz: 041ce68408938affab9523541f1adb283a6c279e69d3b8abdcc6842c47154da1d0441dcb8a44a05f71c1ed382e4624c5d3136d45f5340c17ed8e9b8a96533176
|
data/examples/docker_chat.rb
CHANGED
|
@@ -13,21 +13,30 @@
|
|
|
13
13
|
# export OPENAI_API_KEY='your-key-here'
|
|
14
14
|
# ruby examples/docker_chat.rb
|
|
15
15
|
#
|
|
16
|
+
# Debug mode:
|
|
17
|
+
# ruby examples/docker_chat.rb --debug # Enable debug output
|
|
18
|
+
# DOCKER_CHAT_DEBUG=true ruby examples/docker_chat.rb # Via environment variable
|
|
19
|
+
#
|
|
16
20
|
# Commands:
|
|
17
21
|
# /exit - Exit the chat
|
|
18
22
|
# /help - Show available Docker tools
|
|
19
23
|
# /tools - List all loaded tools
|
|
20
24
|
# /clear - Clear the screen
|
|
25
|
+
# /debug - Toggle debug mode on/off
|
|
21
26
|
# anything else - Send to OpenAI with Docker tools available
|
|
22
27
|
|
|
23
28
|
require_relative '../lib/ruby_llm/docker'
|
|
24
29
|
require 'io/console'
|
|
25
30
|
|
|
26
31
|
# rubocop:disable Metrics/ClassLength
|
|
32
|
+
|
|
33
|
+
# Interactive Docker chat interface using RubyLLM and OpenAI.
|
|
34
|
+
# Provides natural language interaction with Docker containers, images, networks, and volumes.
|
|
27
35
|
class DockerChat
|
|
28
36
|
def initialize
|
|
29
37
|
check_environment
|
|
30
38
|
configure_ruby_llm
|
|
39
|
+
setup_debug_mode
|
|
31
40
|
setup_chat
|
|
32
41
|
@running = true
|
|
33
42
|
end
|
|
@@ -54,8 +63,29 @@ class DockerChat
|
|
|
54
63
|
end
|
|
55
64
|
end
|
|
56
65
|
|
|
66
|
+
def setup_debug_mode
|
|
67
|
+
# Check for debug mode via environment variable or command line argument
|
|
68
|
+
@debug_mode = ENV['DOCKER_CHAT_DEBUG'] == 'true' || ARGV.include?('--debug') || ARGV.include?('-d')
|
|
69
|
+
debug_puts '🐛 Debug mode enabled' if @debug_mode
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def debug_puts(message)
|
|
73
|
+
puts message if @debug_mode
|
|
74
|
+
end
|
|
75
|
+
|
|
57
76
|
def setup_chat
|
|
77
|
+
# rubocop:disable Layout/BlockAlignment
|
|
78
|
+
# rubocop:disable Style/MultilineBlockChain
|
|
58
79
|
@chat = RubyLLM.chat(model: 'gpt-4')
|
|
80
|
+
.on_tool_call do |tool_call|
|
|
81
|
+
debug_puts "🔧 Calling tool: #{tool_call.name}"
|
|
82
|
+
debug_puts "📝 Arguments: #{tool_call.arguments}"
|
|
83
|
+
end
|
|
84
|
+
.on_tool_result do |result|
|
|
85
|
+
debug_puts "✅ Tool returned: #{result}"
|
|
86
|
+
end
|
|
87
|
+
# rubocop:enable Layout/BlockAlignment
|
|
88
|
+
# rubocop:enable Style/MultilineBlockChain
|
|
59
89
|
|
|
60
90
|
# Add all Docker tools to the chat
|
|
61
91
|
RubyLLM::Docker.add_all_tools_to_chat(@chat)
|
|
@@ -78,9 +108,11 @@ class DockerChat
|
|
|
78
108
|
puts ' /help - Show available Docker tools'
|
|
79
109
|
puts ' /tools - List all loaded tools'
|
|
80
110
|
puts ' /clear - Clear the screen'
|
|
111
|
+
puts ' /debug - Toggle debug mode on/off'
|
|
81
112
|
puts
|
|
82
113
|
puts '🚀 Ready! Type your questions or commands...'
|
|
83
114
|
puts
|
|
115
|
+
debug_puts '🐛 Debug mode is currently enabled. Use /debug to toggle.'
|
|
84
116
|
end
|
|
85
117
|
|
|
86
118
|
def main_loop
|
|
@@ -118,6 +150,8 @@ class DockerChat
|
|
|
118
150
|
show_tools
|
|
119
151
|
when '/clear', '/c'
|
|
120
152
|
clear_screen
|
|
153
|
+
when '/debug', '/d'
|
|
154
|
+
toggle_debug_mode
|
|
121
155
|
when input.start_with?('/')
|
|
122
156
|
puts "❓ Unknown command: #{input}"
|
|
123
157
|
puts ' Type /help for available commands'
|
|
@@ -208,6 +242,13 @@ class DockerChat
|
|
|
208
242
|
puts '🐳 Docker Chat - Screen cleared'
|
|
209
243
|
end
|
|
210
244
|
|
|
245
|
+
def toggle_debug_mode
|
|
246
|
+
@debug_mode = !@debug_mode
|
|
247
|
+
status = @debug_mode ? 'enabled' : 'disabled'
|
|
248
|
+
puts "🐛 Debug mode #{status}"
|
|
249
|
+
debug_puts 'Debug output will now be shown for tool calls and results' if @debug_mode
|
|
250
|
+
end
|
|
251
|
+
|
|
211
252
|
def show_goodbye
|
|
212
253
|
puts "\n👋 Thanks for using Docker Chat!"
|
|
213
254
|
puts ' Hope you found it helpful for managing your Docker environment.'
|
|
@@ -2,92 +2,90 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Docker
|
|
5
|
-
#
|
|
5
|
+
# MCP tool for building Docker images.
|
|
6
6
|
#
|
|
7
|
-
# This tool provides the ability to build Docker images
|
|
8
|
-
# content
|
|
9
|
-
#
|
|
7
|
+
# This tool provides the ability to build Docker images from Dockerfile
|
|
8
|
+
# content. It creates custom images by executing Dockerfile instructions
|
|
9
|
+
# and supports comprehensive build configuration including tagging and
|
|
10
|
+
# build arguments.
|
|
11
|
+
#
|
|
12
|
+
# == Features
|
|
13
|
+
#
|
|
14
|
+
# - Build images from Dockerfile content strings
|
|
15
|
+
# - Support for custom image tagging
|
|
16
|
+
# - Comprehensive build output and error reporting
|
|
17
|
+
# - Handles all standard Dockerfile instructions
|
|
18
|
+
# - Build context management
|
|
19
|
+
# - Progress tracking and logging
|
|
10
20
|
#
|
|
11
21
|
# == Security Considerations
|
|
12
22
|
#
|
|
13
|
-
#
|
|
14
|
-
# - Dockerfile commands execute
|
|
15
|
-
# -
|
|
16
|
-
# -
|
|
17
|
-
# -
|
|
18
|
-
# -
|
|
23
|
+
# Image building involves significant security risks:
|
|
24
|
+
# - **Code Execution**: Dockerfile RUN commands execute arbitrary code
|
|
25
|
+
# - **Network Access**: Build process can access networks and repositories
|
|
26
|
+
# - **File System Access**: Can read local files and directories
|
|
27
|
+
# - **Credential Exposure**: May expose build-time secrets and credentials
|
|
28
|
+
# - **Supply Chain Risk**: Downloaded packages may contain malware
|
|
29
|
+
# - **Resource Consumption**: Builds can consume significant CPU, memory, and storage
|
|
19
30
|
#
|
|
20
|
-
# Security
|
|
21
|
-
# - Review Dockerfile content
|
|
22
|
-
# - Use trusted base images
|
|
31
|
+
# **Security Recommendations**:
|
|
32
|
+
# - Review all Dockerfile content before building
|
|
33
|
+
# - Use trusted base images only
|
|
34
|
+
# - Avoid embedding secrets in image layers
|
|
35
|
+
# - Implement build isolation and sandboxing
|
|
36
|
+
# - Monitor build resource consumption
|
|
23
37
|
# - Scan built images for vulnerabilities
|
|
24
|
-
# -
|
|
25
|
-
# - Avoid including secrets in Dockerfile instructions
|
|
26
|
-
# - Use multi-stage builds to minimize final image size
|
|
38
|
+
# - Use multi-stage builds to minimize attack surface
|
|
27
39
|
#
|
|
28
|
-
# ==
|
|
40
|
+
# == Parameters
|
|
29
41
|
#
|
|
30
|
-
# -
|
|
31
|
-
# -
|
|
32
|
-
# - Comprehensive error handling
|
|
33
|
-
# - Support for all standard Dockerfile instructions
|
|
34
|
-
# - Returns image ID and build status
|
|
42
|
+
# - **dockerfile**: Dockerfile content as a string (required)
|
|
43
|
+
# - **tag**: Tag for the built image (optional, e.g., "myimage:latest")
|
|
35
44
|
#
|
|
36
45
|
# == Example Usage
|
|
37
46
|
#
|
|
38
|
-
# #
|
|
39
|
-
#
|
|
47
|
+
# # Build simple image
|
|
48
|
+
# dockerfile_content = <<~DOCKERFILE
|
|
49
|
+
# FROM alpine:latest
|
|
50
|
+
# RUN apk add --no-cache curl
|
|
51
|
+
# CMD ["curl", "--version"]
|
|
52
|
+
# DOCKERFILE
|
|
53
|
+
#
|
|
54
|
+
# response = BuildImage.call(
|
|
40
55
|
# server_context: context,
|
|
41
|
-
# dockerfile:
|
|
56
|
+
# dockerfile: dockerfile_content,
|
|
57
|
+
# tag: "my-curl:latest"
|
|
42
58
|
# )
|
|
43
59
|
#
|
|
44
|
-
# # Build
|
|
45
|
-
#
|
|
60
|
+
# # Build web server image
|
|
61
|
+
# dockerfile_content = <<~DOCKERFILE
|
|
62
|
+
# FROM nginx:alpine
|
|
63
|
+
# COPY nginx.conf /etc/nginx/nginx.conf
|
|
64
|
+
# EXPOSE 80
|
|
65
|
+
# CMD ["nginx", "-g", "daemon off;"]
|
|
66
|
+
# DOCKERFILE
|
|
67
|
+
#
|
|
68
|
+
# response = BuildImage.call(
|
|
46
69
|
# server_context: context,
|
|
47
70
|
# dockerfile: dockerfile_content,
|
|
48
|
-
# tag: "
|
|
71
|
+
# tag: "custom-nginx:v1.0"
|
|
49
72
|
# )
|
|
50
73
|
#
|
|
51
|
-
# @see Docker::Image.
|
|
52
|
-
# @see TagImage
|
|
74
|
+
# @see ::Docker::Image.build_from_dir
|
|
53
75
|
# @since 0.1.0
|
|
54
|
-
|
|
76
|
+
BUILD_IMAGE_DEFINITION = ToolForge.define(:build_image) do
|
|
55
77
|
description 'Build a Docker image'
|
|
56
78
|
|
|
57
|
-
param :dockerfile,
|
|
58
|
-
|
|
79
|
+
param :dockerfile,
|
|
80
|
+
type: :string,
|
|
81
|
+
description: 'Dockerfile content as a string'
|
|
59
82
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#
|
|
67
|
-
# @param dockerfile [String] the complete Dockerfile content as a string
|
|
68
|
-
# @param server_context [Object] RubyLLM context (unused but required)
|
|
69
|
-
# @param tag [String, nil] optional tag to apply to the built image
|
|
70
|
-
#
|
|
71
|
-
# @return [RubyLLM::Tool::Response] build results including image ID and tag info
|
|
72
|
-
#
|
|
73
|
-
# @raise [Docker::Error] for Docker daemon communication errors
|
|
74
|
-
# @raise [StandardError] for build failures or other errors
|
|
75
|
-
#
|
|
76
|
-
# @example Build simple image
|
|
77
|
-
# dockerfile = <<~DOCKERFILE
|
|
78
|
-
# FROM alpine:latest
|
|
79
|
-
# RUN apk add --no-cache nginx
|
|
80
|
-
# EXPOSE 80
|
|
81
|
-
# CMD ["nginx", "-g", "daemon off;"]
|
|
82
|
-
# DOCKERFILE
|
|
83
|
-
#
|
|
84
|
-
# response = tool.execute(
|
|
85
|
-
# dockerfile: dockerfile,
|
|
86
|
-
# tag: "my-nginx:latest"
|
|
87
|
-
# )
|
|
88
|
-
#
|
|
89
|
-
# @see Docker::Image.build
|
|
90
|
-
def execute(dockerfile:, tag: nil)
|
|
83
|
+
param :tag,
|
|
84
|
+
type: :string,
|
|
85
|
+
description: 'Tag for the built image (e.g., "myimage:latest")',
|
|
86
|
+
required: false
|
|
87
|
+
|
|
88
|
+
execute do |dockerfile:, tag: nil|
|
|
91
89
|
# Build the image
|
|
92
90
|
image = ::Docker::Image.build(dockerfile)
|
|
93
91
|
|
|
@@ -101,11 +99,12 @@ module RubyLLM
|
|
|
101
99
|
|
|
102
100
|
response_text = "Image built successfully. ID: #{image.id}"
|
|
103
101
|
response_text += ", Tag: #{tag}" if tag
|
|
104
|
-
|
|
105
102
|
response_text
|
|
106
103
|
rescue StandardError => e
|
|
107
104
|
"Error building image: #{e.message}"
|
|
108
105
|
end
|
|
109
106
|
end
|
|
107
|
+
|
|
108
|
+
BuildImage = BUILD_IMAGE_DEFINITION.to_ruby_llm_tool
|
|
110
109
|
end
|
|
111
110
|
end
|
|
@@ -2,122 +2,124 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Docker
|
|
5
|
-
#
|
|
5
|
+
# MCP tool for copying files and directories to Docker containers.
|
|
6
6
|
#
|
|
7
|
-
# This tool provides the ability to copy files
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
7
|
+
# This tool provides the ability to copy files and directories from the
|
|
8
|
+
# local host filesystem into running Docker containers. It supports both
|
|
9
|
+
# individual files and entire directory trees, with optional ownership
|
|
10
|
+
# modification within the container.
|
|
11
11
|
#
|
|
12
|
-
# ==
|
|
12
|
+
# == Features
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
# -
|
|
16
|
-
# -
|
|
17
|
-
# -
|
|
18
|
-
# -
|
|
19
|
-
# -
|
|
14
|
+
# - Copy files and directories from host to container
|
|
15
|
+
# - Supports recursive directory copying
|
|
16
|
+
# - Preserves file permissions and metadata
|
|
17
|
+
# - Optional ownership modification after copy
|
|
18
|
+
# - Works with running containers
|
|
19
|
+
# - Comprehensive error handling and validation
|
|
20
20
|
#
|
|
21
|
-
# Security
|
|
22
|
-
# - Validate source paths to prevent directory traversal
|
|
23
|
-
# - Ensure containers run with minimal privileges
|
|
24
|
-
# - Monitor file copy operations for sensitive paths
|
|
25
|
-
# - Use read-only filesystems where possible
|
|
26
|
-
# - Implement proper access controls on source files
|
|
21
|
+
# == Security Considerations
|
|
27
22
|
#
|
|
28
|
-
#
|
|
23
|
+
# File copying operations have significant security implications:
|
|
24
|
+
# - **File System Access**: Reads local host file system content
|
|
25
|
+
# - **Container Modification**: Alters container file system state
|
|
26
|
+
# - **Data Injection**: Can introduce malicious files into containers
|
|
27
|
+
# - **Permission Escalation**: May affect container security context
|
|
28
|
+
# - **Resource Consumption**: Large copies can consume storage and I/O
|
|
29
|
+
# - **Path Traversal**: Improper paths could access unintended locations
|
|
30
|
+
#
|
|
31
|
+
# **Security Recommendations**:
|
|
32
|
+
# - Validate and sanitize all file paths
|
|
33
|
+
# - Implement access controls for source file locations
|
|
34
|
+
# - Monitor file copy operations and sizes
|
|
35
|
+
# - Use read-only mounts where possible
|
|
36
|
+
# - Apply resource limits to prevent abuse
|
|
37
|
+
#
|
|
38
|
+
# == Parameters
|
|
29
39
|
#
|
|
30
|
-
# -
|
|
31
|
-
# -
|
|
32
|
-
# -
|
|
33
|
-
# -
|
|
34
|
-
# - Support for both absolute and relative paths
|
|
40
|
+
# - **id**: Container ID or name (required)
|
|
41
|
+
# - **source_path**: Path to file/directory on local filesystem (required)
|
|
42
|
+
# - **destination_path**: Path inside container for copied content (required)
|
|
43
|
+
# - **owner**: Owner for copied files (optional, e.g., "1000:1000" or "username:group")
|
|
35
44
|
#
|
|
36
45
|
# == Example Usage
|
|
37
46
|
#
|
|
38
|
-
# # Copy
|
|
39
|
-
# CopyToContainer.call(
|
|
47
|
+
# # Copy single file
|
|
48
|
+
# response = CopyToContainer.call(
|
|
40
49
|
# server_context: context,
|
|
41
50
|
# id: "web-server",
|
|
42
|
-
# source_path: "/
|
|
43
|
-
# destination_path: "/etc/nginx/"
|
|
51
|
+
# source_path: "/local/config.conf",
|
|
52
|
+
# destination_path: "/etc/nginx/nginx.conf"
|
|
44
53
|
# )
|
|
45
54
|
#
|
|
46
55
|
# # Copy directory with ownership change
|
|
47
|
-
# CopyToContainer.call(
|
|
56
|
+
# response = CopyToContainer.call(
|
|
48
57
|
# server_context: context,
|
|
49
58
|
# id: "app-container",
|
|
50
|
-
# source_path: "/
|
|
51
|
-
# destination_path: "/app
|
|
52
|
-
# owner: "
|
|
59
|
+
# source_path: "/local/app-data",
|
|
60
|
+
# destination_path: "/var/lib/app",
|
|
61
|
+
# owner: "app:app"
|
|
53
62
|
# )
|
|
54
63
|
#
|
|
55
|
-
# @see Docker::Container#archive_in_stream
|
|
64
|
+
# @see ::Docker::Container#archive_in_stream
|
|
56
65
|
# @since 0.1.0
|
|
57
|
-
|
|
66
|
+
COPY_TO_CONTAINER_DEFINITION = ToolForge.define(:copy_to_container) do
|
|
58
67
|
description 'Copy a file or directory from the local filesystem into a running Docker container. ' \
|
|
59
68
|
'The source path is on the local machine, and the destination path is inside the container.'
|
|
60
69
|
|
|
61
|
-
param :id,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
param :
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# destination_path: "/app/",
|
|
104
|
-
# owner: "www-data:www-data"
|
|
105
|
-
# )
|
|
106
|
-
#
|
|
107
|
-
# @see Docker::Container#archive_in_stream
|
|
108
|
-
# @see #add_to_tar
|
|
109
|
-
def execute(id:, source_path:, destination_path:, owner: nil)
|
|
70
|
+
param :id,
|
|
71
|
+
type: :string,
|
|
72
|
+
description: 'Container ID or name'
|
|
73
|
+
|
|
74
|
+
param :source_path,
|
|
75
|
+
type: :string,
|
|
76
|
+
description: 'Path to the file or directory on the local filesystem to copy'
|
|
77
|
+
|
|
78
|
+
param :destination_path,
|
|
79
|
+
type: :string,
|
|
80
|
+
description: 'Path inside the container where the file/directory should be copied'
|
|
81
|
+
|
|
82
|
+
param :owner,
|
|
83
|
+
type: :string,
|
|
84
|
+
description: 'Owner for the copied files (optional, e.g., "1000:1000" or "username:group")',
|
|
85
|
+
required: false
|
|
86
|
+
|
|
87
|
+
# Helper method for adding files/directories to tar
|
|
88
|
+
class_helper :add_to_tar do |tar, path, archive_path|
|
|
89
|
+
if File.directory?(path)
|
|
90
|
+
# Add directory entry
|
|
91
|
+
tar.mkdir(archive_path, File.stat(path).mode)
|
|
92
|
+
|
|
93
|
+
# Add directory contents
|
|
94
|
+
Dir.entries(path).each do |entry|
|
|
95
|
+
next if ['.', '..'].include?(entry)
|
|
96
|
+
|
|
97
|
+
full_path = File.join(path, entry)
|
|
98
|
+
archive_entry_path = File.join(archive_path, entry)
|
|
99
|
+
add_to_tar(tar, full_path, archive_entry_path)
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
# Add file
|
|
103
|
+
File.open(path, 'rb') do |file|
|
|
104
|
+
tar.add_file_simple(archive_path, File.stat(path).mode, file.size) do |tar_file|
|
|
105
|
+
IO.copy_stream(file, tar_file)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
execute do |id:, source_path:, destination_path:, owner: nil|
|
|
110
112
|
container = ::Docker::Container.get(id)
|
|
111
113
|
|
|
112
114
|
# Verify source path exists
|
|
113
|
-
|
|
115
|
+
next "Source path not found: #{source_path}" unless File.exist?(source_path)
|
|
114
116
|
|
|
115
117
|
# Create a tar archive of the source
|
|
116
118
|
tar_io = StringIO.new
|
|
117
119
|
tar_io.set_encoding('ASCII-8BIT')
|
|
118
120
|
|
|
119
121
|
Gem::Package::TarWriter.new(tar_io) do |tar|
|
|
120
|
-
|
|
122
|
+
add_to_tar(tar, source_path, File.basename(source_path))
|
|
121
123
|
end
|
|
122
124
|
|
|
123
125
|
tar_io.rewind
|
|
@@ -136,61 +138,14 @@ module RubyLLM
|
|
|
136
138
|
file_type = File.directory?(source_path) ? 'directory' : 'file'
|
|
137
139
|
response_text = "Successfully copied #{file_type} from #{source_path} to #{id}:#{destination_path}"
|
|
138
140
|
response_text += "\nOwnership changed to #{owner}" if owner
|
|
139
|
-
|
|
140
141
|
response_text
|
|
141
142
|
rescue ::Docker::Error::NotFoundError
|
|
142
143
|
"Container #{id} not found"
|
|
143
144
|
rescue StandardError => e
|
|
144
145
|
"Error copying to container: #{e.message}"
|
|
145
146
|
end
|
|
146
|
-
|
|
147
|
-
# Recursively add files and directories to a tar archive.
|
|
148
|
-
#
|
|
149
|
-
# This helper method builds a tar archive by recursively traversing
|
|
150
|
-
# the filesystem starting from the given path. It preserves file
|
|
151
|
-
# permissions and handles both files and directories appropriately.
|
|
152
|
-
#
|
|
153
|
-
# For directories, it creates directory entries in the tar and then
|
|
154
|
-
# recursively processes all contained files and subdirectories.
|
|
155
|
-
# For files, it reads the content and adds it to the tar with
|
|
156
|
-
# preserved permissions.
|
|
157
|
-
#
|
|
158
|
-
# @param tar [Gem::Package::TarWriter] the tar writer instance
|
|
159
|
-
# @param path [String] the filesystem path to add to the archive
|
|
160
|
-
# @param archive_path [String] the path within the tar archive
|
|
161
|
-
#
|
|
162
|
-
# @return [void]
|
|
163
|
-
#
|
|
164
|
-
# @example Add single file
|
|
165
|
-
# add_to_tar(tar_writer, "/host/file.txt", "file.txt")
|
|
166
|
-
#
|
|
167
|
-
# @example Add directory tree
|
|
168
|
-
# add_to_tar(tar_writer, "/host/mydir", "mydir")
|
|
169
|
-
#
|
|
170
|
-
# @see Gem::Package::TarWriter#mkdir
|
|
171
|
-
# @see Gem::Package::TarWriter#add_file_simple
|
|
172
|
-
def self.add_to_tar(tar, path, archive_path)
|
|
173
|
-
if File.directory?(path)
|
|
174
|
-
# Add directory entry
|
|
175
|
-
tar.mkdir(archive_path, File.stat(path).mode)
|
|
176
|
-
|
|
177
|
-
# Add directory contents
|
|
178
|
-
Dir.entries(path).each do |entry|
|
|
179
|
-
next if ['.', '..'].include?(entry)
|
|
180
|
-
|
|
181
|
-
full_path = File.join(path, entry)
|
|
182
|
-
archive_entry_path = File.join(archive_path, entry)
|
|
183
|
-
add_to_tar(tar, full_path, archive_entry_path)
|
|
184
|
-
end
|
|
185
|
-
else
|
|
186
|
-
# Add file
|
|
187
|
-
File.open(path, 'rb') do |file|
|
|
188
|
-
tar.add_file_simple(archive_path, File.stat(path).mode, file.size) do |tar_file|
|
|
189
|
-
IO.copy_stream(file, tar_file)
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
147
|
end
|
|
148
|
+
|
|
149
|
+
CopyToContainer = COPY_TO_CONTAINER_DEFINITION.to_ruby_llm_tool
|
|
195
150
|
end
|
|
196
151
|
end
|