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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/task.md +10 -0
  3. data/Gemfile.lock +1 -1
  4. data/components/agent/.rubocop.yml +1 -0
  5. data/components/agent/Dockerfile +43 -0
  6. data/components/agent/Dockerfile.dev +38 -0
  7. data/components/agent/Gemfile +15 -0
  8. data/components/agent/Makefile +67 -0
  9. data/components/agent/bin/langop-agent +140 -0
  10. data/components/agent/config/config.yaml +47 -0
  11. data/components/base/Dockerfile +34 -0
  12. data/components/base/Makefile +42 -0
  13. data/components/base/entrypoint.sh +12 -0
  14. data/components/base/gem-credentials +2 -0
  15. data/components/tool/.gitignore +10 -0
  16. data/components/tool/.rubocop.yml +19 -0
  17. data/components/tool/.yardopts +7 -0
  18. data/components/tool/Dockerfile +44 -0
  19. data/components/tool/Dockerfile.dev +39 -0
  20. data/components/tool/Gemfile +18 -0
  21. data/components/tool/Makefile +77 -0
  22. data/components/tool/README.md +145 -0
  23. data/components/tool/config.ru +4 -0
  24. data/components/tool/examples/calculator.rb +63 -0
  25. data/components/tool/examples/example_tool.rb +190 -0
  26. data/components/tool/lib/langop/dsl.rb +20 -0
  27. data/components/tool/server.rb +7 -0
  28. data/lib/language_operator/agent/task_executor.rb +39 -7
  29. data/lib/language_operator/cli/commands/agent.rb +0 -3
  30. data/lib/language_operator/cli/commands/system.rb +1 -0
  31. data/lib/language_operator/cli/formatters/log_formatter.rb +19 -67
  32. data/lib/language_operator/cli/formatters/log_style.rb +151 -0
  33. data/lib/language_operator/cli/formatters/progress_formatter.rb +10 -6
  34. data/lib/language_operator/logger.rb +3 -8
  35. data/lib/language_operator/templates/agent_synthesis.tmpl +35 -28
  36. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
  37. data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
  38. data/lib/language_operator/version.rb +1 -1
  39. data/synth/001/README.md +72 -0
  40. data/synth/001/output.log +13 -13
  41. data/synth/002/Makefile +12 -0
  42. data/synth/002/README.md +287 -0
  43. data/synth/002/agent.rb +23 -0
  44. data/synth/002/agent.txt +1 -0
  45. data/synth/002/output.log +22 -0
  46. metadata +33 -3
  47. data/synth/Makefile +0 -39
  48. data/synth/README.md +0 -342
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '014892e52c89c77009fbcdc0dde8449e2c839ef18ddad8f7dac4ed393e294a8b'
4
- data.tar.gz: 240d5cf62f332fa83535cb8739b4149c6e3aae3d191b3cb5f21645e847db0d27
3
+ metadata.gz: 6a7f1f7e5550c2118f096f8dad4eaba602d36c99bee49ed7c0bdf09b6c3a5098
4
+ data.tar.gz: e1c4b977724e311aadc28788d4d83affb87cc0bc21c34c423984e6fe8c3a5b79
5
5
  SHA512:
6
- metadata.gz: b0bfc1d43409d369e848d448cd5a5c9cb21a0f85bd41d7cdf993be322f5be43b0e9aabdd39d7979701bf8a41f4476bf0502782af4eb712fb8bc669d4568384e0
7
- data.tar.gz: 0bd79c7a91e1b5d59904cdb1467fc0c507e62993d9cb676ce202d23d218f3ecfa6c0ffe299959cf47c6ed82b7147eb1ffe1170069f0f083f2c4e68bf90dd1962
6
+ metadata.gz: 2261ac662c454529aa1e56e2ae3c59bcd989f523a314ff5a4cf8b769a727fb9544def42d570271c9529761d9fd32366347f89262922ecef43e0f73f2fbe56409
7
+ data.tar.gz: dc31892136d0702e32c6e107b50c8a90d5b439bacc32e59015c84f2490196526177f5825c04537a3c0ef1d6b65f9367ba3a340ee14fcd0ca7f9dceca77e7ce55
@@ -0,0 +1,10 @@
1
+ # Task Commmand
2
+
3
+ ## Inputs
4
+
5
+ - :task str - name of task to run
6
+ - :persona str (optional) - persona to assume
7
+
8
+ ## Instructions
9
+
10
+ You have been instructed to read and execute a task. The task definitions are found in ./requirements/tasks/:name.md.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- language-operator (0.1.46)
4
+ language-operator (0.1.47)
5
5
  faraday (~> 2.0)
6
6
  k8s-ruby (~> 0.17)
7
7
  mcp (~> 0.4)
@@ -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,2 @@
1
+ ---
2
+ https://git.theryans.io/api/packages/language-operator/rubygems: Bearer b579c250d4ac8223641f5f0178024c896b542812
@@ -0,0 +1,10 @@
1
+ # YARD documentation
2
+ /doc/
3
+ /.yardoc/
4
+
5
+ # Bundler
6
+ /vendor/bundle/
7
+ /.bundle/
8
+
9
+ # RuboCop cache
10
+ /.rubocop-*
@@ -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,7 @@
1
+ --markup markdown
2
+ --readme README.md
3
+ --output-dir doc
4
+ --protected
5
+ --private
6
+ lib/**/*.rb
7
+ server.rb
@@ -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