language-operator 0.1.46 → 0.1.47
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/.claude/commands/task.md +10 -0
- data/Gemfile.lock +1 -1
- data/components/agent/.rubocop.yml +1 -0
- data/components/agent/Dockerfile +43 -0
- data/components/agent/Dockerfile.dev +38 -0
- data/components/agent/Gemfile +15 -0
- data/components/agent/Makefile +67 -0
- data/components/agent/bin/langop-agent +140 -0
- data/components/agent/config/config.yaml +47 -0
- data/components/base/Dockerfile +34 -0
- data/components/base/Makefile +42 -0
- data/components/base/entrypoint.sh +12 -0
- data/components/base/gem-credentials +2 -0
- data/components/tool/.gitignore +10 -0
- data/components/tool/.rubocop.yml +19 -0
- data/components/tool/.yardopts +7 -0
- data/components/tool/Dockerfile +44 -0
- data/components/tool/Dockerfile.dev +39 -0
- data/components/tool/Gemfile +18 -0
- data/components/tool/Makefile +77 -0
- data/components/tool/README.md +145 -0
- data/components/tool/config.ru +4 -0
- data/components/tool/examples/calculator.rb +63 -0
- data/components/tool/examples/example_tool.rb +190 -0
- data/components/tool/lib/langop/dsl.rb +20 -0
- data/components/tool/server.rb +7 -0
- data/lib/language_operator/agent/task_executor.rb +39 -7
- data/lib/language_operator/cli/commands/agent.rb +0 -3
- data/lib/language_operator/cli/commands/system.rb +1 -0
- data/lib/language_operator/cli/formatters/log_formatter.rb +19 -67
- data/lib/language_operator/cli/formatters/log_style.rb +151 -0
- data/lib/language_operator/cli/formatters/progress_formatter.rb +10 -6
- data/lib/language_operator/logger.rb +3 -8
- data/lib/language_operator/templates/agent_synthesis.tmpl +35 -28
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
- data/lib/language_operator/version.rb +1 -1
- data/synth/001/README.md +72 -0
- data/synth/001/output.log +13 -13
- data/synth/002/Makefile +12 -0
- data/synth/002/README.md +287 -0
- data/synth/002/agent.rb +23 -0
- data/synth/002/agent.txt +1 -0
- data/synth/002/output.log +22 -0
- metadata +33 -3
- data/synth/Makefile +0 -39
- data/synth/README.md +0 -342
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a7f1f7e5550c2118f096f8dad4eaba602d36c99bee49ed7c0bdf09b6c3a5098
|
|
4
|
+
data.tar.gz: e1c4b977724e311aadc28788d4d83affb87cc0bc21c34c423984e6fe8c3a5b79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2261ac662c454529aa1e56e2ae3c59bcd989f523a314ff5a4cf8b769a727fb9544def42d570271c9529761d9fd32366347f89262922ecef43e0f73f2fbe56409
|
|
7
|
+
data.tar.gz: dc31892136d0702e32c6e107b50c8a90d5b439bacc32e59015c84f2490196526177f5825c04537a3c0ef1d6b65f9367ba3a340ee14fcd0ca7f9dceca77e7ce55
|
data/Gemfile.lock
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
inherit_from: ../../.rubocop.yml
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
FROM ghcr.io/language-operator/base:latest
|
|
2
|
+
|
|
3
|
+
# Install agent-specific packages (Ruby deps inherited from base)
|
|
4
|
+
RUN apk add --no-cache jq
|
|
5
|
+
|
|
6
|
+
WORKDIR /app/agent
|
|
7
|
+
|
|
8
|
+
# Copy Gemfile and install dependencies
|
|
9
|
+
COPY Gemfile /app/agent/
|
|
10
|
+
|
|
11
|
+
# Copy local gem if building in CI (will be downloaded from artifact)
|
|
12
|
+
# Use wildcard with --chown to avoid failure if file doesn't exist
|
|
13
|
+
COPY --chown=root:root language-operator-*.gem* /tmp/
|
|
14
|
+
|
|
15
|
+
# Install local gem first if it exists, then install bundle dependencies
|
|
16
|
+
RUN if ls /tmp/language-operator-*.gem 1> /dev/null 2>&1; then \
|
|
17
|
+
echo "Installing local language-operator gem..." && \
|
|
18
|
+
gem install /tmp/language-operator-*.gem && \
|
|
19
|
+
rm /tmp/language-operator-*.gem*; \
|
|
20
|
+
fi && \
|
|
21
|
+
bundle install --no-cache
|
|
22
|
+
|
|
23
|
+
# Copy default agent configuration
|
|
24
|
+
COPY config/ /app/agent/config/
|
|
25
|
+
|
|
26
|
+
# Copy entrypoint script
|
|
27
|
+
COPY bin/langop-agent /usr/local/bin/langop-agent
|
|
28
|
+
RUN chmod +x /usr/local/bin/langop-agent
|
|
29
|
+
|
|
30
|
+
# Set ownership
|
|
31
|
+
RUN chown -R langop:langop /app/agent
|
|
32
|
+
|
|
33
|
+
# Switch to langop user
|
|
34
|
+
USER langop
|
|
35
|
+
|
|
36
|
+
# Set environment defaults
|
|
37
|
+
ENV WORKSPACE_PATH="/workspace" \
|
|
38
|
+
AGENT_MODE="autonomous" \
|
|
39
|
+
AGENT_CODE_PATH="/etc/agent/code/agent.rb" \
|
|
40
|
+
AGENT_FALLBACK_MODE="error"
|
|
41
|
+
|
|
42
|
+
# Default command - auto-loads synthesized code from ConfigMap
|
|
43
|
+
CMD ["/usr/local/bin/langop-agent"]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
FROM ghcr.io/language-operator/base:latest
|
|
2
|
+
|
|
3
|
+
# Install agent-specific packages (Ruby deps inherited from base)
|
|
4
|
+
RUN apk add --no-cache jq
|
|
5
|
+
|
|
6
|
+
WORKDIR /app/agent
|
|
7
|
+
|
|
8
|
+
# Copy the local gem file
|
|
9
|
+
COPY language-operator-*.gem /tmp/
|
|
10
|
+
|
|
11
|
+
# Copy Gemfile and install dependencies with development group
|
|
12
|
+
COPY Gemfile /app/agent/
|
|
13
|
+
|
|
14
|
+
# Install the local gem directly instead of using bundler groups
|
|
15
|
+
RUN gem install /tmp/language-operator-*.gem && \
|
|
16
|
+
bundle install --no-cache
|
|
17
|
+
|
|
18
|
+
# Copy default agent configuration
|
|
19
|
+
COPY config/ /app/agent/config/
|
|
20
|
+
|
|
21
|
+
# Copy entrypoint script
|
|
22
|
+
COPY bin/langop-agent /usr/local/bin/langop-agent
|
|
23
|
+
RUN chmod +x /usr/local/bin/langop-agent
|
|
24
|
+
|
|
25
|
+
# Set ownership
|
|
26
|
+
RUN chown -R langop:langop /app/agent
|
|
27
|
+
|
|
28
|
+
# Switch to langop user
|
|
29
|
+
USER langop
|
|
30
|
+
|
|
31
|
+
# Set environment defaults
|
|
32
|
+
ENV WORKSPACE_PATH="/workspace" \
|
|
33
|
+
AGENT_MODE="autonomous" \
|
|
34
|
+
AGENT_CODE_PATH="/etc/agent/code/agent.rb" \
|
|
35
|
+
AGENT_FALLBACK_MODE="error"
|
|
36
|
+
|
|
37
|
+
# Default command - auto-loads synthesized code from ConfigMap
|
|
38
|
+
CMD ["/usr/local/bin/langop-agent"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
gem 'language-operator', '~> 0.1.46'
|
|
6
|
+
|
|
7
|
+
# Agent-specific dependencies for autonomous execution
|
|
8
|
+
gem 'concurrent-ruby', '~> 1.3'
|
|
9
|
+
gem 'rufus-scheduler', '~> 3.9'
|
|
10
|
+
|
|
11
|
+
group :development do
|
|
12
|
+
gem 'rubocop', '~> 1.60'
|
|
13
|
+
gem 'rubocop-performance', '~> 1.20'
|
|
14
|
+
gem 'yard', '~> 0.9.37'
|
|
15
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Image configuration
|
|
2
|
+
IMAGE_NAME := ghcr.io/language-operator/agent
|
|
3
|
+
IMAGE_TAG := latest
|
|
4
|
+
IMAGE_FULL := $(IMAGE_NAME):$(IMAGE_TAG)
|
|
5
|
+
|
|
6
|
+
# Include shared targets
|
|
7
|
+
include ../../Makefile.common
|
|
8
|
+
|
|
9
|
+
# Default target
|
|
10
|
+
.PHONY: help
|
|
11
|
+
help:
|
|
12
|
+
@echo "Available targets:"
|
|
13
|
+
@echo " build - Build the Docker image"
|
|
14
|
+
@echo " build-dev - Build the Docker image with local gem"
|
|
15
|
+
@echo " push-dev - Build and push dev image to all k8s nodes"
|
|
16
|
+
@echo " build-all - Build all inherited images in order (base→agent)"
|
|
17
|
+
@echo " scan - Scan the Docker image with Trivy"
|
|
18
|
+
@echo " shell - Run interactive shell in container"
|
|
19
|
+
@echo " run - Run the agent framework"
|
|
20
|
+
@echo " test - Run tests (linting)"
|
|
21
|
+
@echo " lint - Run RuboCop linter"
|
|
22
|
+
@echo " doc - Generate YARD documentation"
|
|
23
|
+
@echo " clean - Clean generated files"
|
|
24
|
+
|
|
25
|
+
# Build development image with local gem
|
|
26
|
+
.PHONY: build-dev
|
|
27
|
+
build-dev:
|
|
28
|
+
@echo "Building local gem..."
|
|
29
|
+
cd ../../../language-operator-gem && gem build language-operator.gemspec
|
|
30
|
+
@echo "Copying gem to build context..."
|
|
31
|
+
cp ../../../language-operator-gem/*.gem .
|
|
32
|
+
@echo "Building Docker image..."
|
|
33
|
+
docker build -f Dockerfile.dev -t $(IMAGE_NAME):dev -t $(IMAGE_NAME):$(IMAGE_TAG)-dev .
|
|
34
|
+
@echo "Cleaning up gem file..."
|
|
35
|
+
rm language-operator-*.gem
|
|
36
|
+
|
|
37
|
+
# Push development image to all k8s cluster nodes (containerd/k3s)
|
|
38
|
+
.PHONY: push-dev
|
|
39
|
+
push-dev: build-dev
|
|
40
|
+
@echo "Pushing development image to all k8s nodes..."
|
|
41
|
+
@for node in $$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do \
|
|
42
|
+
echo "==> Pushing to node: $$node"; \
|
|
43
|
+
docker save $(IMAGE_NAME):dev | ssh $$node 'sudo k3s ctr images import -' || echo "Failed to push to $$node"; \
|
|
44
|
+
done
|
|
45
|
+
@echo "✓ Development image pushed to all nodes!"
|
|
46
|
+
|
|
47
|
+
# Build all inherited images in dependency order
|
|
48
|
+
.PHONY: build-all
|
|
49
|
+
build-all:
|
|
50
|
+
@echo "Building inherited image chain: base → agent"
|
|
51
|
+
@echo ""
|
|
52
|
+
@echo "==> Building base image..."
|
|
53
|
+
cd ../base && $(MAKE) build
|
|
54
|
+
@echo ""
|
|
55
|
+
@echo "==> Building agent image..."
|
|
56
|
+
$(MAKE) build
|
|
57
|
+
@echo ""
|
|
58
|
+
@echo "✓ All images built successfully!"
|
|
59
|
+
|
|
60
|
+
# Run the agent framework
|
|
61
|
+
.PHONY: run
|
|
62
|
+
run:
|
|
63
|
+
docker run --rm -it \
|
|
64
|
+
-v $(PWD)/config:/app/agent/config \
|
|
65
|
+
-e WORKSPACE_PATH=/workspace \
|
|
66
|
+
-e AGENT_MODE=autonomous \
|
|
67
|
+
$(IMAGE_FULL)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Langop Agent Entrypoint
|
|
5
|
+
#
|
|
6
|
+
# This script auto-loads synthesized agent code from a ConfigMap volume
|
|
7
|
+
# or falls back to default agent behavior if no code is provided.
|
|
8
|
+
#
|
|
9
|
+
# The operator synthesizes agent DSL code from natural language instructions
|
|
10
|
+
# and mounts it as a ConfigMap at AGENT_CODE_PATH.
|
|
11
|
+
|
|
12
|
+
# Disable output buffering for immediate log visibility
|
|
13
|
+
$stdout.sync = true
|
|
14
|
+
$stderr.sync = true
|
|
15
|
+
|
|
16
|
+
require 'language_operator'
|
|
17
|
+
|
|
18
|
+
# Wait for tool sidecars to be ready
|
|
19
|
+
def wait_for_tools(config)
|
|
20
|
+
return unless config['mcp_servers']
|
|
21
|
+
|
|
22
|
+
require 'net/http'
|
|
23
|
+
require 'uri'
|
|
24
|
+
|
|
25
|
+
servers = config['mcp_servers'] || []
|
|
26
|
+
servers.each do |server|
|
|
27
|
+
url = server['url']
|
|
28
|
+
next unless url&.start_with?('http')
|
|
29
|
+
|
|
30
|
+
uri = URI.parse(url)
|
|
31
|
+
max_attempts = 30
|
|
32
|
+
delay = 1
|
|
33
|
+
|
|
34
|
+
puts "Waiting for tool at #{uri.host}:#{uri.port}..."
|
|
35
|
+
|
|
36
|
+
max_attempts.times do |attempt|
|
|
37
|
+
# Try to connect to the health endpoint or root
|
|
38
|
+
Net::HTTP.start(uri.host, uri.port, open_timeout: 1, read_timeout: 1) do |http|
|
|
39
|
+
http.head('/')
|
|
40
|
+
end
|
|
41
|
+
puts "Tool ready at #{uri.host}:#{uri.port}"
|
|
42
|
+
break
|
|
43
|
+
rescue StandardError
|
|
44
|
+
if attempt < max_attempts - 1
|
|
45
|
+
sleep(delay)
|
|
46
|
+
else
|
|
47
|
+
puts "Tool at #{uri.host}:#{uri.port} not ready after #{max_attempts} attempts"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Configuration
|
|
54
|
+
code_path = ENV.fetch('AGENT_CODE_PATH', '/etc/agent/code/agent.rb')
|
|
55
|
+
fallback_mode = ENV.fetch('AGENT_FALLBACK_MODE', 'error') # error | autonomous | interactive
|
|
56
|
+
|
|
57
|
+
# Check if synthesized code exists
|
|
58
|
+
if File.exist?(code_path)
|
|
59
|
+
begin
|
|
60
|
+
# Load the synthesized DSL code using the agent loader
|
|
61
|
+
# This provides the DSL context with the 'agent' method
|
|
62
|
+
LanguageOperator::Dsl.load_agent_file(code_path)
|
|
63
|
+
|
|
64
|
+
# Get the agent from the registry
|
|
65
|
+
agents = LanguageOperator::Dsl.agent_registry.all
|
|
66
|
+
if agents.empty?
|
|
67
|
+
puts 'No agent defined in code'
|
|
68
|
+
exit 1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
agent_def = agents.first
|
|
72
|
+
|
|
73
|
+
# Execute the agent based on its definition using DSL v1 flow
|
|
74
|
+
config = LanguageOperator::Client::Config.from_env
|
|
75
|
+
agent = LanguageOperator::Agent::Base.new(config)
|
|
76
|
+
|
|
77
|
+
# Wait for tool sidecars to be ready before connecting
|
|
78
|
+
wait_for_tools(config)
|
|
79
|
+
|
|
80
|
+
# Use the proper agent execution flow that handles DSL v1 main blocks, tasks, and output handlers
|
|
81
|
+
LanguageOperator::Agent.run_with_definition(agent, agent_def)
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
puts "Error loading agent code: #{e.message}"
|
|
84
|
+
puts e.backtrace.take(5).join("\n")
|
|
85
|
+
|
|
86
|
+
case fallback_mode
|
|
87
|
+
when 'autonomous'
|
|
88
|
+
puts 'Falling back to autonomous mode with default config'
|
|
89
|
+
fallback_to_autonomous
|
|
90
|
+
when 'interactive'
|
|
91
|
+
puts 'Falling back to interactive mode'
|
|
92
|
+
fallback_to_interactive
|
|
93
|
+
else
|
|
94
|
+
puts 'No fallback configured, exiting'
|
|
95
|
+
exit 1
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
else
|
|
100
|
+
puts "No synthesized code found at #{code_path}"
|
|
101
|
+
|
|
102
|
+
case fallback_mode
|
|
103
|
+
when 'autonomous'
|
|
104
|
+
puts 'Starting in autonomous mode with default config'
|
|
105
|
+
fallback_to_autonomous
|
|
106
|
+
when 'interactive'
|
|
107
|
+
puts 'Starting in interactive mode'
|
|
108
|
+
fallback_to_interactive
|
|
109
|
+
else
|
|
110
|
+
puts 'No agent code provided and no fallback configured'
|
|
111
|
+
puts 'The operator should have created a ConfigMap with synthesized code'
|
|
112
|
+
puts 'Check LanguageAgent status for synthesis errors'
|
|
113
|
+
exit 1
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Fallback implementations
|
|
118
|
+
def fallback_to_autonomous
|
|
119
|
+
config = LanguageOperator::Client::Config.load_with_fallback
|
|
120
|
+
agent = LanguageOperator::Agent::Base.new(config)
|
|
121
|
+
|
|
122
|
+
# Get instructions from environment
|
|
123
|
+
instructions = ENV.fetch('AGENT_INSTRUCTIONS', nil)
|
|
124
|
+
|
|
125
|
+
if instructions && !instructions.empty?
|
|
126
|
+
agent.connect!
|
|
127
|
+
|
|
128
|
+
# Simple autonomous loop
|
|
129
|
+
executor = LanguageOperator::Agent::Executor.new(agent)
|
|
130
|
+
executor.execute(instructions)
|
|
131
|
+
else
|
|
132
|
+
puts 'No instructions provided in AGENT_INSTRUCTIONS'
|
|
133
|
+
exit 1
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def fallback_to_interactive
|
|
138
|
+
puts 'Interactive mode not yet implemented'
|
|
139
|
+
exit 1
|
|
140
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Agent Configuration Example
|
|
2
|
+
#
|
|
3
|
+
# This configuration file defines the behavior of a language agent.
|
|
4
|
+
|
|
5
|
+
# LLM Configuration
|
|
6
|
+
llm:
|
|
7
|
+
provider: openai_compatible # openai, anthropic, or openai_compatible
|
|
8
|
+
model: llama3.2
|
|
9
|
+
endpoint: http://model-proxy:8080/v1
|
|
10
|
+
# api_key: optional for local models
|
|
11
|
+
|
|
12
|
+
# MCP Server Connections
|
|
13
|
+
mcp_servers:
|
|
14
|
+
- name: default-tools
|
|
15
|
+
url: http://tools:80/mcp
|
|
16
|
+
transport: streamable
|
|
17
|
+
enabled: true
|
|
18
|
+
description: "Default tool server"
|
|
19
|
+
|
|
20
|
+
# Agent-specific configuration
|
|
21
|
+
agent:
|
|
22
|
+
# Instructions for the agent
|
|
23
|
+
instructions: |
|
|
24
|
+
You are a helpful autonomous agent.
|
|
25
|
+
Monitor your workspace and complete assigned tasks.
|
|
26
|
+
|
|
27
|
+
# Execution mode: autonomous, interactive, scheduled, event-driven
|
|
28
|
+
mode: autonomous
|
|
29
|
+
|
|
30
|
+
# Maximum iterations for autonomous mode (prevents infinite loops)
|
|
31
|
+
max_iterations: 100
|
|
32
|
+
|
|
33
|
+
# Schedules for scheduled mode (cron format)
|
|
34
|
+
schedules:
|
|
35
|
+
- cron: "0 6 * * *" # Daily at 6:00 AM
|
|
36
|
+
task: "Check for updates and report status"
|
|
37
|
+
|
|
38
|
+
# - cron: "0 * * * *" # Every hour
|
|
39
|
+
# task: "Monitor system health"
|
|
40
|
+
|
|
41
|
+
# Workspace configuration
|
|
42
|
+
workspace:
|
|
43
|
+
enabled: true
|
|
44
|
+
path: /workspace
|
|
45
|
+
|
|
46
|
+
# Debug mode
|
|
47
|
+
debug: false
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
FROM alpine:3
|
|
2
|
+
|
|
3
|
+
ENV LANGOP_VERSION=0.1
|
|
4
|
+
ENV LANGOP_USER=langop
|
|
5
|
+
|
|
6
|
+
# Install tini for proper init and su-exec for user switching
|
|
7
|
+
RUN apk add --no-cache tini su-exec
|
|
8
|
+
|
|
9
|
+
# Install common Ruby dependencies for all components
|
|
10
|
+
RUN apk add --no-cache \
|
|
11
|
+
ruby \
|
|
12
|
+
ruby-dev \
|
|
13
|
+
ruby-bundler \
|
|
14
|
+
build-base \
|
|
15
|
+
curl \
|
|
16
|
+
ca-certificates \
|
|
17
|
+
tzdata
|
|
18
|
+
|
|
19
|
+
# Create the langop user (but don't switch to it yet - let child images do their work first)
|
|
20
|
+
RUN addgroup -S langop && adduser -S langop -G langop
|
|
21
|
+
|
|
22
|
+
# Create gem directories for bundler
|
|
23
|
+
RUN mkdir -p /root/.gem /home/langop/.gem && \
|
|
24
|
+
chown -R langop:langop /home/langop/.gem
|
|
25
|
+
|
|
26
|
+
# Copy entrypoint script
|
|
27
|
+
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
28
|
+
RUN chmod +x /usr/local/bin/entrypoint.sh
|
|
29
|
+
|
|
30
|
+
# Use tini as PID 1, which will run our entrypoint script
|
|
31
|
+
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]
|
|
32
|
+
|
|
33
|
+
# Default command
|
|
34
|
+
CMD ["/bin/sh"]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Image configuration
|
|
2
|
+
IMAGE_NAME := ghcr.io/language-operator/base
|
|
3
|
+
IMAGE_TAG := latest
|
|
4
|
+
IMAGE_FULL := $(IMAGE_NAME):$(IMAGE_TAG)
|
|
5
|
+
|
|
6
|
+
# Include shared targets
|
|
7
|
+
include ../../Makefile.common
|
|
8
|
+
|
|
9
|
+
# Default target
|
|
10
|
+
.PHONY: help
|
|
11
|
+
help:
|
|
12
|
+
@echo "Available targets:"
|
|
13
|
+
@echo " build - Build the Docker image"
|
|
14
|
+
@echo " push - Push the Docker image to the registry"
|
|
15
|
+
@echo " scan - Scan the Docker image with Trivy"
|
|
16
|
+
@echo " test - Test the Docker image"
|
|
17
|
+
@echo " shell - Run the image and exec into it with an interactive shell"
|
|
18
|
+
@echo " run - Run the container"
|
|
19
|
+
@echo " env - Display sorted list of environment variables in the image"
|
|
20
|
+
|
|
21
|
+
# Push the Docker image to the registry
|
|
22
|
+
.PHONY: push
|
|
23
|
+
push:
|
|
24
|
+
docker push $(IMAGE_FULL)
|
|
25
|
+
|
|
26
|
+
# Override test to include base-specific tests
|
|
27
|
+
.PHONY: test
|
|
28
|
+
test:
|
|
29
|
+
@echo "Testing base image..."
|
|
30
|
+
docker run --rm $(IMAGE_FULL) ruby --version
|
|
31
|
+
docker run --rm $(IMAGE_FULL) bundle --version
|
|
32
|
+
@echo "All tests passed!"
|
|
33
|
+
|
|
34
|
+
# Run the container
|
|
35
|
+
.PHONY: run
|
|
36
|
+
run:
|
|
37
|
+
docker run --rm -it $(IMAGE_FULL)
|
|
38
|
+
|
|
39
|
+
# Display sorted list of environment variables in the image
|
|
40
|
+
.PHONY: env
|
|
41
|
+
env:
|
|
42
|
+
docker run --rm $(IMAGE_FULL) /bin/sh -c 'env | sort'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# Entrypoint script for langop/base image
|
|
4
|
+
|
|
5
|
+
# If running as root and LANGOP_USER is set, switch to that user
|
|
6
|
+
if [ "$(id -u)" = "0" ] && [ -n "$LANGOP_USER" ]; then
|
|
7
|
+
# Use su-exec for minimal overhead user switching
|
|
8
|
+
exec su-exec "$LANGOP_USER" "$@"
|
|
9
|
+
else
|
|
10
|
+
# Already running as non-root user
|
|
11
|
+
exec "$@"
|
|
12
|
+
fi
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
inherit_from: ../../.rubocop.yml
|
|
2
|
+
|
|
3
|
+
# Component-specific overrides
|
|
4
|
+
Style/FrozenStringLiteralComment:
|
|
5
|
+
Enabled: false
|
|
6
|
+
|
|
7
|
+
Style/StringLiterals:
|
|
8
|
+
EnforcedStyle: single_quotes
|
|
9
|
+
|
|
10
|
+
Metrics/MethodLength:
|
|
11
|
+
Max: 20
|
|
12
|
+
|
|
13
|
+
Metrics/BlockLength:
|
|
14
|
+
Exclude:
|
|
15
|
+
- '*.rb'
|
|
16
|
+
- 'examples/**/*.rb'
|
|
17
|
+
|
|
18
|
+
Metrics/AbcSize:
|
|
19
|
+
Max: 20
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
FROM ghcr.io/language-operator/base:latest
|
|
2
|
+
|
|
3
|
+
# Ruby dependencies inherited from base image
|
|
4
|
+
|
|
5
|
+
# Create application directory
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Copy Gemfile and install dependencies
|
|
9
|
+
COPY Gemfile /app/
|
|
10
|
+
|
|
11
|
+
# Copy local gem if building in CI (will be downloaded from artifact)
|
|
12
|
+
# Use wildcard with --chown to avoid failure if file doesn't exist
|
|
13
|
+
COPY --chown=root:root language-operator-*.gem* /tmp/
|
|
14
|
+
|
|
15
|
+
# Install local gem first if it exists, then install bundle dependencies
|
|
16
|
+
RUN if ls /tmp/language-operator-*.gem 1> /dev/null 2>&1; then \
|
|
17
|
+
echo "Installing local language-operator gem..." && \
|
|
18
|
+
gem install /tmp/language-operator-*.gem && \
|
|
19
|
+
rm /tmp/language-operator-*.gem*; \
|
|
20
|
+
fi && \
|
|
21
|
+
bundle install --no-cache
|
|
22
|
+
|
|
23
|
+
# Copy application files
|
|
24
|
+
COPY server.rb /app/
|
|
25
|
+
COPY lib/ /app/lib/
|
|
26
|
+
|
|
27
|
+
# Create /mcp directory for tool definitions
|
|
28
|
+
RUN mkdir -p /mcp && chown -R langop:langop /mcp
|
|
29
|
+
|
|
30
|
+
# Ensure app directory is owned by langop user
|
|
31
|
+
RUN chown -R langop:langop /app
|
|
32
|
+
|
|
33
|
+
# Expose default port
|
|
34
|
+
EXPOSE 80
|
|
35
|
+
|
|
36
|
+
# Set environment variables
|
|
37
|
+
ENV PORT=80
|
|
38
|
+
ENV RACK_ENV=production
|
|
39
|
+
|
|
40
|
+
# Switch to langop user
|
|
41
|
+
USER langop
|
|
42
|
+
|
|
43
|
+
# Start the server
|
|
44
|
+
CMD ["ruby", "server.rb"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
FROM ghcr.io/language-operator/base:latest
|
|
2
|
+
|
|
3
|
+
# Ruby dependencies inherited from base image
|
|
4
|
+
|
|
5
|
+
# Create application directory
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Copy the local gem file
|
|
9
|
+
COPY language-operator-*.gem /tmp/
|
|
10
|
+
|
|
11
|
+
# Copy Gemfile and install dependencies
|
|
12
|
+
COPY Gemfile /app/
|
|
13
|
+
|
|
14
|
+
# Install the local gem directly then install other dependencies
|
|
15
|
+
RUN gem install /tmp/language-operator-*.gem && \
|
|
16
|
+
bundle install --no-cache
|
|
17
|
+
|
|
18
|
+
# Copy application files
|
|
19
|
+
COPY server.rb /app/
|
|
20
|
+
COPY lib/ /app/lib/
|
|
21
|
+
|
|
22
|
+
# Create /mcp directory for tool definitions
|
|
23
|
+
RUN mkdir -p /mcp && chown -R langop:langop /mcp
|
|
24
|
+
|
|
25
|
+
# Ensure app directory is owned by langop user
|
|
26
|
+
RUN chown -R langop:langop /app
|
|
27
|
+
|
|
28
|
+
# Expose default port
|
|
29
|
+
EXPOSE 80
|
|
30
|
+
|
|
31
|
+
# Set environment variables
|
|
32
|
+
ENV PORT=80
|
|
33
|
+
ENV RACK_ENV=production
|
|
34
|
+
|
|
35
|
+
# Switch to langop user
|
|
36
|
+
USER langop
|
|
37
|
+
|
|
38
|
+
# Start the server
|
|
39
|
+
CMD ["ruby", "server.rb"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
gem 'language-operator', '~> 0.1.46'
|
|
6
|
+
|
|
7
|
+
# Tool server dependencies
|
|
8
|
+
gem 'json', '~> 2.7'
|
|
9
|
+
gem 'puma', '~> 6.4'
|
|
10
|
+
gem 'rackup', '~> 2.1'
|
|
11
|
+
gem 'sinatra', '~> 4.0'
|
|
12
|
+
gem 'sinatra-contrib', '~> 4.0'
|
|
13
|
+
|
|
14
|
+
group :development do
|
|
15
|
+
gem 'rubocop', '~> 1.60'
|
|
16
|
+
gem 'rubocop-performance', '~> 1.20'
|
|
17
|
+
gem 'yard', '~> 0.9.37'
|
|
18
|
+
end
|