Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
repos:
# General hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
exclude: \.md$|\.rst$
Expand All @@ -19,23 +19,23 @@ repos:

# Python - Black formatter
- repo: https://github.com/psf/black
rev: 24.10.0
rev: 26.1.0
hooks:
- id: black
files: ^aenv/
args: [--line-length=88]

# Python - isort import sorter
- repo: https://github.com/pycqa/isort
rev: 5.13.2
rev: 7.0.0
hooks:
- id: isort
files: ^aenv/
args: [--profile=black]

# Python - Ruff linter
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.2
rev: v0.15.0
hooks:
- id: ruff
files: ^aenv/
Expand All @@ -52,7 +52,7 @@ repos:

# Go - golangci-lint (run per module to support go.work)
- repo: https://github.com/golangci/golangci-lint
rev: v1.62.2
rev: v2.8.0
hooks:
- id: golangci-lint
name: golangci-lint (api-service)
Expand All @@ -75,23 +75,23 @@ repos:

# Markdown - markdownlint
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.43.0
rev: v0.47.0
hooks:
- id: markdownlint
args: [--fix, --disable, MD013, MD033, MD041, --]
exclude: ^aenv/examples/tau2/src/data/

# YAML - yamllint
- repo: https://github.com/adrienverge/yamllint
rev: v1.35.1
rev: v1.38.0
hooks:
- id: yamllint
args: [-d, "{extends: relaxed, rules: {line-length: {max: 120}}}"]
exclude: ^deploy/[^/]+/templates/

# Dockerfile - hadolint
- repo: https://github.com/hadolint/hadolint
rev: v2.12.0
rev: v2.14.0
hooks:
- id: hadolint
files: (?i)(^|/)Dockerfile(\..*)?$|\.Dockerfile$
Expand Down
1 change: 1 addition & 0 deletions aenv/builtin-envs/terminalbench/src/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
python client.py http://ip:port/mcp # Custom HTTP endpoint
python client.py ./server.py # Use STDIO transport
"""

import asyncio
import json
import sys
Expand Down
1 change: 1 addition & 0 deletions aenv/builtin-envs/terminalbench/src/tmuxsession.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
tmux_session.py
Pure nerdctl version TmuxSession, supports automatic container ID resolution via "ns+container name fragment"
"""

from __future__ import annotations

import json
Expand Down
26 changes: 26 additions & 0 deletions aenv/src/aenv/core/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,32 @@ async def _create_env_instance(self):
f"{self._log_prefix()} Environment instance created with ID: {self._instance.id}"
)

# Update URLs to use instance's actual IP/data_url
if self._instance.data_url:
# Use data_url from API response if available (Docker mode)
self.aenv_data_url = self._instance.data_url
# Extract base URL (remove /mcp path)
base_url = self.aenv_data_url.rsplit("/mcp", 1)[0]
self.aenv_health_url = base_url + "/health"
self.aenv_reward_url = base_url + "/task/reward"
self.aenv_functions_base_url = base_url + "/functions"
logger.info(
f"{self._log_prefix()} Using data_url from API: {self.aenv_data_url}"
)
elif self._instance.ip:
# Fallback to IP-based URL construction (K8s mode)
self.aenv_data_url = make_mcp_url(self._instance.ip, 8081, "/mcp")
self.aenv_health_url = make_mcp_url(self._instance.ip, 8081, "/health")
self.aenv_reward_url = make_mcp_url(
self._instance.ip, 8081, "/task/reward"
)
self.aenv_functions_base_url = make_mcp_url(
self._instance.ip, 8081, "/functions"
)
logger.info(
f"{self._log_prefix()} Constructed data_url from IP: {self.aenv_data_url}"
)

await self.wait_for_ready(timeout=self._startup_timeout)
logger.info(f"{self._log_prefix()} Environment ready: {self.env_name}")

Expand Down
1 change: 1 addition & 0 deletions aenv/src/aenv/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class EnvInstance(BaseModel):
created_at: str = Field(description="Creation time")
updated_at: str = Field(description="Update time")
ip: Optional[str] = Field(None, description="Instance IP")
data_url: Optional[str] = Field(None, description="MCP data endpoint URL")


class EnvInstanceCreateRequest(BaseModel):
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/cmds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
Command module
"""

# mycli/commands/__init__.py

from cli.cmds.build import build
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/cmds/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
Uses HTTP API for control plane operations (list, get, delete)
Uses Environment SDK for deployment operations (create)
"""

import asyncio
import json
import os
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/cmds/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
list command - List all environments with pagination
"""

import json

import click
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/cmds/pull.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
pull command - Pull an aenv project to local repository
"""

import os

import click
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/cmds/push.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
push command - Push current aenv project to remote backend aenv hub
"""

import json
import os

Expand Down
5 changes: 4 additions & 1 deletion aenv/src/cli/cmds/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- service delete: Delete a service
- service update: Update service (replicas, image, env vars)
"""

import asyncio
import json
import os
Expand Down Expand Up @@ -346,7 +347,9 @@ def create(
console.print(f" - Mount Path: {final_mount_path}")
else:
console.print(" - Mount Path: /home/admin/data (default)")
console.print(" [yellow]⚠️ With storage enabled, replicas must be 1[/yellow]")
console.print(
" [yellow]⚠️ With storage enabled, replicas must be 1[/yellow]"
)
else:
console.print(
"[dim] Storage: Disabled (use --enable-storage to enable storage)[/dim]"
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/cmds/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
version command - Display version information
"""

import json
from importlib.resources import files

Expand Down
Empty file added aenv/src/cli/data/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions aenv/src/cli/data/version_info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"version": "0.1.4",
"build_date": "2026-02-07",
"git_commit": "",
"git_branch": "",
"python_version": ">=3.10",
"description": "AEnvironment CLI - Manage containerized AI agent environments"
}
1 change: 1 addition & 0 deletions aenv/src/cli/tests/test_build_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"""
Test script for the build command
"""

import json
import subprocess
import sys
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/tests/test_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
Test script for instances command
"""

import json
import os
from unittest.mock import Mock, patch
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/tests/test_singleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"""
Test script for AEnvHubClient singleton implementation
"""

import sys

# Add the src directory to Python path
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
Utility module
"""

from .archive_tool import ArchiveTool, quick_cleanup, quick_pack

__all__ = ["ArchiveTool", "quick_pack", "quick_cleanup"]
1 change: 1 addition & 0 deletions aenv/src/cli/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
Configuration utility
"""

import os
from pathlib import Path

Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/utils/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
Scaffolding tool - for project initialization
"""

import dataclasses
import json
import shutil
Expand Down
1 change: 1 addition & 0 deletions aenv/src/cli/utils/table_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

Provides beautiful, colorful table displays inspired by modern CLI tools.
"""

from typing import Any, Dict, List, Optional

from rich.console import Console
Expand Down
8 changes: 5 additions & 3 deletions api-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Build stage
FROM golang:1.21-alpine AS builder

RUN apk add --no-cache git
RUN apk add --no-cache git ca-certificates

WORKDIR /workspace

Expand All @@ -19,15 +19,17 @@ COPY api-service/go.mod api-service/go.sum ./api-service/
COPY envhub/go.mod envhub/go.sum ./envhub/
COPY controller/go.mod controller/go.sum ./controller/

# Download dependencies (this layer will be cached if go.mod/go.sum don't change)
# Download dependencies with workspace support
# Note: Go workspace mode requires modules to be present, so we download from workspace root
WORKDIR /workspace
ENV GOWORK=/workspace/go.work
WORKDIR /workspace/api-service
RUN set -e; \
if ! go mod download -x; then \
echo "Download failed, retrying..."; \
sleep 5; \
go mod download; \
fi

WORKDIR /workspace

# Copy source code after dependencies are downloaded
Expand Down
32 changes: 28 additions & 4 deletions api-service/controller/env_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package controller

import (
"fmt"

"api-service/models"
backendmodels "envhub/models"

Expand Down Expand Up @@ -72,11 +74,33 @@ func (ctrl *EnvInstanceController) CreateEnvInstance(c *gin.Context) {
backendmodels.JSONErrorWithMessage(c, 400, "Invalid EnvName format: "+err.Error())
return
}
backendEnv, err := ctrl.backendClient.GetEnvByVersion(name, version)
if err != nil {
backendmodels.JSONErrorWithMessage(c, 500, "Failed to find environment: "+err.Error())
return

// Try to get environment from backend
var backendEnv *backendmodels.Env
if ctrl.backendClient != nil {
backendEnv, err = ctrl.backendClient.GetEnvByVersion(name, version)
if err != nil {
// If backend is not available or env not found, create a default env for Docker mode
log.Infof("Backend not available or env not found, using default config for: %s@%s", name, version)
backendEnv = &backendmodels.Env{
Name: name,
Version: version,
DeployConfig: map[string]interface{}{
"imageName": fmt.Sprintf("aenv/%s:%s", name, version),
},
}
}
} else {
// No backend client, create default env
backendEnv = &backendmodels.Env{
Name: name,
Version: version,
DeployConfig: map[string]interface{}{
"imageName": fmt.Sprintf("aenv/%s:%s", name, version),
},
}
}
Comment on lines +80 to 102
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for creating a default backendEnv is duplicated in the if err != nil block and the else block. This can be refactored to reduce redundancy and improve maintainability. Consider a single point of creation for the default environment if backendEnv is nil after the initial attempt to fetch it from the backend.


if backendEnv == nil {
backendmodels.JSONErrorWithMessage(c, 404, "Environment not found: "+req.EnvName)
return
Expand Down
31 changes: 16 additions & 15 deletions api-service/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ require (
github.com/gin-gonic/gin v1.9.1
github.com/go-redis/redis/v8 v8.11.5
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/client_golang v1.16.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/pflag v1.0.6-0.20200504143853-81378bbcd8a1
github.com/spf13/pflag v1.0.9
go.uber.org/zap v1.27.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
k8s.io/apimachinery v0.30.0-alpha.2
)

replace (
envhub => ../envhub
)
replace envhub => ../envhub

require (
github.com/beorn7/perks v1.0.1 // indirect
Expand All @@ -32,27 +31,29 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading