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
161 changes: 161 additions & 0 deletions camel/toolkits/terminal_toolkit/go_runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========

r"""Go runtime auto-installation utilities.

This module provides functionality to detect, download, and configure
the Go programming language runtime. If Go is not already available
on the system, it is downloaded from the official Go website and
extracted to ``~/.camel/runtimes/go/``.
"""

import os
import shutil
import subprocess
from typing import Optional

from camel.logger import get_logger

from .runtime_utils import (
GO_CHECKSUM_URL_TEMPLATE,
GO_DOWNLOAD_URL_TEMPLATE,
GO_VERSION,
RUNTIMES_DIR,
ArchiveType,
RuntimeDownloadError,
RuntimeExtractionError,
RuntimeInstallError,
UpdateCallback,
download_and_extract_runtime,
get_platform_info,
)

logger = get_logger(__name__)


def ensure_go_available(
update_callback: UpdateCallback = None,
) -> Optional[str]:
r"""Ensure Go is available, downloading it if necessary.

Checks if Go is already installed on the system. If not, downloads
the official Go binary for the current platform and extracts it to
``~/.camel/runtimes/go/``.

Args:
update_callback (Callable[[str], None], optional): Callback
function to receive status updates.
(default: :obj:`None`)

Returns:
Optional[str]: The path to the Go ``bin`` directory that
should be added to PATH, or ``None`` if Go could not
be made available.
"""
# Check if Go is already available on PATH
existing_go = shutil.which("go")
if existing_go is not None:
try:
result = subprocess.run(
["go", "version"],
check=False,
capture_output=True,
text=True,
timeout=10,
)
if result.returncode == 0:
version_info = result.stdout.strip()
info = (
f"Go is already available: {version_info}"
)
if update_callback:
update_callback(f"{info}\n")
return os.path.dirname(existing_go)
except Exception:
pass

# Go not found, attempt auto-install
if update_callback:
update_callback(
f"Go not found, installing Go {GO_VERSION}...\n"
)

try:
os_name, arch = get_platform_info()
except RuntimeError as e:
if update_callback:
update_callback(
f"Cannot auto-install Go: {e}\n"
)
return None

# Construct download URL from template
ext: ArchiveType = (
"zip" if os_name == "windows" else "tar.gz"
)
url = GO_DOWNLOAD_URL_TEMPLATE.format(
version=GO_VERSION, os_name=os_name, arch=arch, ext=ext,
)
checksum_url = GO_CHECKSUM_URL_TEMPLATE.format(
version=GO_VERSION, os_name=os_name, arch=arch, ext=ext,
)

go_runtime_dir = os.path.join(RUNTIMES_DIR, "go")

# Check if already downloaded
go_bin_dir = os.path.join(go_runtime_dir, "go", "bin")
go_executable = (
"go.exe" if os_name == "windows" else "go"
)
if os.path.exists(os.path.join(go_bin_dir, go_executable)):
info = (
f"Go {GO_VERSION} is already installed at "
f"{go_runtime_dir}"
)
if update_callback:
update_callback(f"{info}\n")
return go_bin_dir

try:
download_and_extract_runtime(
url=url,
target_dir=go_runtime_dir,
archive_type=ext,
checksum_url=checksum_url,
update_callback=update_callback,
)
except (
RuntimeDownloadError,
RuntimeExtractionError,
RuntimeInstallError,
) as e:
logger.error(f"Failed to install Go: {e}")
if update_callback:
update_callback(f"Failed to install Go: {e}\n")
return None

if os.path.exists(os.path.join(go_bin_dir, go_executable)):
if update_callback:
update_callback(
f"Go {GO_VERSION} installed successfully at "
f"{go_runtime_dir}\n"
)
return go_bin_dir

if update_callback:
update_callback(
"Failed to install Go. "
"If needed, please install it manually.\n"
)
return None
219 changes: 219 additions & 0 deletions camel/toolkits/terminal_toolkit/java_runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========

r"""Java runtime auto-installation utilities.

This module provides functionality to detect, download, and configure
the Java (JDK) runtime. If Java is not already available on the
system, Eclipse Temurin (Adoptium) JDK 21 LTS is downloaded and
extracted to ``~/.camel/runtimes/java/``.
"""

import os
import shutil
import subprocess
from typing import Optional

from camel.logger import get_logger

from .runtime_utils import (
ADOPTIUM_DOWNLOAD_URL_TEMPLATE,
JAVA_VERSION,
RUNTIMES_DIR,
ArchiveType,
RuntimeDownloadError,
RuntimeExtractionError,
RuntimeInstallError,
SupportedOS,
UpdateCallback,
download_and_extract_runtime,
get_platform_info,
)

logger = get_logger(__name__)


def ensure_java_available(
update_callback: UpdateCallback = None,
) -> Optional[str]:
r"""Ensure Java (JDK) is available, downloading if necessary.

Checks if Java is already installed on the system. If not,
downloads Eclipse Temurin (Adoptium) JDK 21 LTS for the current
platform and extracts it to ``~/.camel/runtimes/java/``.

Args:
update_callback (Callable[[str], None], optional): Callback
function to receive status updates.
(default: :obj:`None`)

Returns:
Optional[str]: The JAVA_HOME directory path, or ``None``
if Java could not be made available.
"""
# Check if Java is already available on PATH
existing_java = shutil.which("java")
if existing_java is not None:
try:
result = subprocess.run(
["java", "-version"],
check=False,
capture_output=True,
text=True,
timeout=10,
)
# java -version outputs to stderr
version_output = (
result.stderr.strip() or result.stdout.strip()
)
if result.returncode == 0:
info = (
f"Java is already available: "
f"{version_output}"
)
if update_callback:
update_callback(f"{info}\n")
return os.path.dirname(
os.path.dirname(existing_java)
)
except Exception:
pass

# Java not found, attempt auto-install
if update_callback:
update_callback(
f"Java not found, installing Adoptium Temurin "
f"JDK {JAVA_VERSION} LTS...\n"
)

try:
os_name, arch = get_platform_info()
except RuntimeError as e:
if update_callback:
update_callback(
f"Cannot auto-install Java: {e}\n"
)
return None

# Map arch/OS names for Adoptium API
adoptium_arch = "x64" if arch == "amd64" else arch
adoptium_os = "mac" if os_name == "darwin" else os_name

# Construct Adoptium API URL from template
url = ADOPTIUM_DOWNLOAD_URL_TEMPLATE.format(
version=JAVA_VERSION,
os_name=adoptium_os,
arch=adoptium_arch,
)

java_runtime_dir = os.path.join(RUNTIMES_DIR, "java")
archive_type: ArchiveType = (
"zip" if os_name == "windows" else "tar.gz"
)

# Check if already downloaded
java_home = _find_java_home(java_runtime_dir, os_name)
if java_home:
info = (
f"Java JDK is already installed at {java_home}"
)
if update_callback:
update_callback(f"{info}\n")
return java_home

try:
download_and_extract_runtime(
url=url,
target_dir=java_runtime_dir,
archive_type=archive_type,
update_callback=update_callback,
)
except (
RuntimeDownloadError,
RuntimeExtractionError,
RuntimeInstallError,
) as e:
logger.error(f"Failed to install Java: {e}")
if update_callback:
update_callback(f"Failed to install Java: {e}\n")
return None

java_home = _find_java_home(java_runtime_dir, os_name)
if java_home:
if update_callback:
update_callback(
f"Adoptium Temurin JDK {JAVA_VERSION} installed "
f"successfully at {java_home}\n"
)
return java_home

if update_callback:
update_callback(
"Failed to install Java. "
"If needed, please install it manually.\n"
)
return None


def _find_java_home(
java_runtime_dir: str, os_name: SupportedOS
) -> Optional[str]:
r"""Find the JAVA_HOME directory within the runtime directory.

Adoptium extracts to a versioned directory like
``jdk-21.0.x+y/``. This helper scans for it.

Args:
java_runtime_dir (str): The parent directory where Java was
extracted.
os_name (SupportedOS): The operating system name.

Returns:
Optional[str]: The path to JAVA_HOME, or None if not found.
"""
if not os.path.exists(java_runtime_dir):
return None

java_executable = (
"java.exe" if os_name == "windows" else "java"
)

for entry in os.listdir(java_runtime_dir):
entry_path = os.path.join(java_runtime_dir, entry)
if not os.path.isdir(entry_path):
continue

# On macOS, the structure is
# jdk-xxx/Contents/Home/bin/java
if os_name == "darwin":
java_path = os.path.join(
entry_path,
"Contents",
"Home",
"bin",
java_executable,
)
if os.path.exists(java_path):
return os.path.join(
entry_path, "Contents", "Home"
)

# On Linux/Windows, the structure is jdk-xxx/bin/java
java_path = os.path.join(
entry_path, "bin", java_executable
)
if os.path.exists(java_path):
return entry_path

return None
Loading
Loading