diff --git a/.build/compile.sh b/.build/compile.sh
new file mode 100644
index 00000000..85a06495
--- /dev/null
+++ b/.build/compile.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+
+# Create a Python virtual environment
+python -m venv compile_env
+
+# Activate the virtual environment
+source compile_env/bin/activate
+
+# Install the current package and pyinstaller
+pip install .. pyinstaller
+
+# Run PyInstaller with the specified options
+pyinstaller ../main.py --onedir --name coretex --copy-metadata readchar --copy-metadata coretex --add-data "../coretex/resources/_coretex.py:coretex/resources" --workpath coretex --noconfirm
+
+# Deactivate and remove the virtual environment
+deactivate
+rm -rf compile_env
+rm -rf coretex.spec
diff --git a/.build/coretex.pkg b/.build/coretex.pkg
new file mode 100644
index 00000000..ae4397ef
Binary files /dev/null and b/.build/coretex.pkg differ
diff --git a/.build/deb-package.sh b/.build/deb-package.sh
new file mode 100644
index 00000000..a2d0945d
--- /dev/null
+++ b/.build/deb-package.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+set -e
+
+# Define package name and directory structure
+PACKAGE_NAME="coretex"
+PACKAGE_DIR="${PACKAGE_NAME}"
+DEBIAN_DIR="${PACKAGE_DIR}/DEBIAN"
+BIN_DIR="${PACKAGE_DIR}/usr/local/bin"
+
+# Create the necessary directory structure
+mkdir -p "${DEBIAN_DIR}"
+mkdir -p "${BIN_DIR}"
+
+# Create a Python virtual environment
+python -m venv compile_env
+
+# Activate the virtual environment
+source compile_env/bin/activate
+
+# Install the toml package (for generate_control.py)
+pip install toml
+
+# Move the entire dist/main directory contents directly to /usr/local/bin
+mv dist/coretex/* "${BIN_DIR}/"
+rm -rf dist/
+
+# Generate the control file using Python (assuming you have the Python script ready)
+python generate_control.py
+
+# Deactivate and remove the virtual environment
+deactivate
+rm -rf compile_env
+
+# Build the .deb package
+dpkg-deb --build "${PACKAGE_DIR}"
+
+# Remove unnecessary dir's
+rm -rf "${PACKAGE_DIR}"
\ No newline at end of file
diff --git a/.build/generate_control.py b/.build/generate_control.py
new file mode 100644
index 00000000..5bc19e6f
--- /dev/null
+++ b/.build/generate_control.py
@@ -0,0 +1,32 @@
+import toml
+
+
+def main() -> None:
+ # Load data from pyproject.toml
+ with open("../pyproject.toml", "r") as f:
+ pyproject = toml.load(f)
+
+ # Extract necessary information
+ packageName = pyproject["project"]["name"]
+ version = pyproject["project"]["version"]
+ description = pyproject["project"]["description"]
+
+ # Create control file content
+ controlContent = (
+ f"Package: {packageName}\n"
+ f"Version: {version}\n"
+ "Section: base\n"
+ "Priority: optional\n"
+ "Architecture: all\n"
+ "Depends: python3\n"
+ "Maintainer: Your Name < your.email@example.com >\n"
+ f"Description: {description}\n"
+ )
+
+ # Write to control file
+ with open(f"{packageName}/DEBIAN/control", "w") as controlFile:
+ controlFile.write(controlContent)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.build/macos-package.sh b/.build/macos-package.sh
new file mode 100644
index 00000000..9df4c5d4
--- /dev/null
+++ b/.build/macos-package.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+set -e
+
+# Extract version from pyproject.toml
+VERSION=$(grep '^version = ' ../pyproject.toml | sed 's/version = "\(.*\)"/\1/')
+
+# Define package name and directory structure
+PACKAGE_NAME="coretex"
+PAYLOAD_DIR="dist/coretex/"
+
+# Build the .pkg package using pkgbuild
+pkgbuild --root "${PAYLOAD_DIR}" --identifier ai.coretex --version "${VERSION}" --install-location "/usr/local/bin" "${PACKAGE_NAME}.pkg"
+
+# Clean up
+rm -rf coretex/
+rm -rf dist/
\ No newline at end of file
diff --git a/.build/windows-package.sh b/.build/windows-package.sh
new file mode 100644
index 00000000..e69de29b
diff --git a/coretex/cli/commands/node.py b/coretex/cli/commands/node.py
index 98dfd63b..e05bc45a 100644
--- a/coretex/cli/commands/node.py
+++ b/coretex/cli/commands/node.py
@@ -24,8 +24,8 @@
from ..modules import node as node_module
from ..modules.node import NodeStatus, getNodeStatus
from ..modules.user import initializeUserSession
-from ..modules.utils import onBeforeCommandExecute, checkEnvironment
-from ..modules.update import activateAutoUpdate
+from ..modules.update import activateAutoUpdate, getNodeStatus
+from ..modules.utils import onBeforeCommandExecute
from ...utils import docker
from ...configuration import NodeConfiguration, InvalidConfiguration, ConfigurationNotFound
@@ -208,8 +208,12 @@ def logs(tail: Optional[int], follow: bool, timestamps: bool) -> None:
@click.group()
@onBeforeCommandExecute(docker.isDockerAvailable, excludeSubcommands = ["status"])
@onBeforeCommandExecute(initializeUserSession)
+<<<<<<< HEAD
+@onBeforeCommandExecute(node_module.checkResourceLimitations)
+=======
@onBeforeCommandExecute(node_module.checkResourceLimitations, excludeSubcommands = ["status"])
@onBeforeCommandExecute(checkEnvironment)
+>>>>>>> develop
def node() -> None:
pass
diff --git a/coretex/cli/modules/cron.py b/coretex/cli/modules/cron.py
index e504f335..48fb03c0 100644
--- a/coretex/cli/modules/cron.py
+++ b/coretex/cli/modules/cron.py
@@ -36,9 +36,9 @@ def jobExists(script: str) -> bool:
return any(script in line for line in existingLines)
-def scheduleJob(scriptName: str) -> None:
+def scheduleJob(commandName: str) -> None:
existingLines = getExisting()
- cronJob = f"*/30 * * * * {CONFIG_DIR / scriptName} >> {CONFIG_DIR}/logs/ctx_autoupdate.log 2>&1\n"
+ cronJob = f"*/30 * * * * {commandName} >> {CONFIG_DIR}/logs/ctx_autoupdate.log 2>&1\n"
existingLines.append(cronJob)
tempCronFilePath = CONFIG_DIR / "temp.cron"
diff --git a/coretex/cli/modules/node.py b/coretex/cli/modules/node.py
index 80696556..ec473892 100644
--- a/coretex/cli/modules/node.py
+++ b/coretex/cli/modules/node.py
@@ -302,7 +302,7 @@ def promptRam(ramLimit: int) -> int:
if nodeRam < config_defaults.MINIMUM_RAM:
ui.errorEcho(
f"ERROR: Configured RAM ({nodeRam}GB) is lower than "
- "the minimum Node RAM requirement ({config_defaults.MINIMUM_RAM}GB)."
+ f"the minimum Node RAM requirement ({config_defaults.MINIMUM_RAM}GB)."
)
return promptRam(ramLimit)
diff --git a/coretex/cli/modules/update.py b/coretex/cli/modules/update.py
index 1250ad84..11330174 100644
--- a/coretex/cli/modules/update.py
+++ b/coretex/cli/modules/update.py
@@ -16,15 +16,10 @@
# along with this program. If not, see .
from enum import IntEnum
-from pathlib import Path
import requests
-from .utils import getExecPath
from .cron import jobExists, scheduleJob
-from ..resources import RESOURCES_DIR
-from ...utils import command
-from ...configuration import DEFAULT_VENV_PATH
UPDATE_SCRIPT_NAME = "update_node.sh"
@@ -39,31 +34,17 @@ class NodeStatus(IntEnum):
reconnecting = 5
-def generateUpdateScript() -> str:
- dockerExecPath = getExecPath("docker")
- gitExecPath = getExecPath("git")
- bashScriptTemplatePath = RESOURCES_DIR / "update_script_template.sh"
-
- with bashScriptTemplatePath.open("r") as scriptFile:
- bashScriptTemplate = scriptFile.read()
-
- return bashScriptTemplate.format(
- dockerPath = dockerExecPath,
- gitPath = gitExecPath,
- venvPath = DEFAULT_VENV_PATH
- )
-
-
-def dumpScript(updateScriptPath: Path) -> None:
- with updateScriptPath.open("w") as scriptFile:
- scriptFile.write(generateUpdateScript())
-
- command(["chmod", "+x", str(updateScriptPath)], ignoreStdout = True)
+def activateAutoUpdate() -> None:
+ commandName = "coretex node update -n"
+ if not jobExists(str(commandName)):
+ scheduleJob(commandName)
-def activateAutoUpdate() -> None:
- updateScriptPath = DEFAULT_VENV_PATH.parent / UPDATE_SCRIPT_NAME
- dumpScript(updateScriptPath)
- if not jobExists(str(updateScriptPath)):
- scheduleJob(UPDATE_SCRIPT_NAME)
+def getNodeStatus() -> NodeStatus:
+ try:
+ response = requests.get(f"http://localhost:21000/status", timeout = 1)
+ status = response.json()["status"]
+ return NodeStatus(status)
+ except:
+ return NodeStatus.inactive
diff --git a/coretex/cli/modules/utils.py b/coretex/cli/modules/utils.py
index ddf96258..52b99f8d 100644
--- a/coretex/cli/modules/utils.py
+++ b/coretex/cli/modules/utils.py
@@ -16,15 +16,10 @@
# along with this program. If not, see .
from typing import List, Any, Tuple, Optional, Callable
-from pathlib import Path
from functools import wraps
from importlib.metadata import version as getLibraryVersion
-import sys
-import venv
-import shutil
import logging
-import platform
from py3nvml import py3nvml
@@ -32,7 +27,6 @@
import requests
from . import ui
-from ...configuration import DEFAULT_VENV_PATH
from ...utils.process import command
@@ -40,51 +34,6 @@ def formatCliVersion(version: Tuple[int, int, int]) -> str:
return ".".join(map(str, version))
-def fetchCtxSource() -> Optional[str]:
- _, output, _ = command([sys.executable, "-m", "pip", "freeze"], ignoreStdout = True, ignoreStderr = True)
- packages = output.splitlines()
-
- for package in packages:
- if "coretex" in package:
- return package.replace(" ", "")
-
- return None
-
-
-def createEnvironment(venvPython: Path) -> None:
- if DEFAULT_VENV_PATH.exists():
- shutil.rmtree(DEFAULT_VENV_PATH)
-
- venv.create(DEFAULT_VENV_PATH, with_pip = True)
-
- if platform.system() == "Windows":
- venvPython = DEFAULT_VENV_PATH / "Scripts" / "python.exe"
-
- ctxSource = fetchCtxSource()
- if ctxSource is not None:
- command([str(venvPython), "-m", "pip", "install", ctxSource], ignoreStdout = True, ignoreStderr = True)
-
-
-def checkEnvironment() -> None:
- venvPython = DEFAULT_VENV_PATH / "bin" / "python"
- venvActivate = DEFAULT_VENV_PATH / "bin" / "activate"
- venvCoretex = DEFAULT_VENV_PATH / "bin" / "coretex"
-
- if not venvActivate.exists() or not venvPython.exists():
- createEnvironment(venvPython)
- return
-
- try:
- command([str(venvCoretex), "version"], check = True, ignoreStderr = True, ignoreStdout = True)
- except Exception:
- createEnvironment(venvPython)
- return
-
-
-def updateLib() -> None:
- command([sys.executable, "-m", "pip", "install", "--no-cache-dir", "--upgrade", "coretex"], ignoreStdout = True, ignoreStderr = True)
-
-
def parseLibraryVersion(version: str) -> Optional[Tuple[int, int, int]]:
parts = version.split(".")
diff --git a/coretex/cli/resources/__init__.py b/coretex/cli/resources/__init__.py
deleted file mode 100644
index 7bf608d8..00000000
--- a/coretex/cli/resources/__init__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (C) 2023 Coretex LLC
-
-# This file is part of Coretex.ai
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-
-from .resources import RESOURCES_DIR
diff --git a/coretex/cli/resources/resources.py b/coretex/cli/resources/resources.py
deleted file mode 100644
index a5101f6e..00000000
--- a/coretex/cli/resources/resources.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (C) 2023 Coretex LLC
-
-# This file is part of Coretex.ai
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-
-from pathlib import Path
-
-
-RESOURCES_DIR = Path(__file__).resolve().parent
diff --git a/coretex/cli/resources/update_script_template.sh b/coretex/cli/resources/update_script_template.sh
deleted file mode 100644
index 417da9ca..00000000
--- a/coretex/cli/resources/update_script_template.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-# Variables
-DOCKER_PATH={dockerPath}
-GIT_PATH={gitPath}
-VENV_PATH={venvPath}
-
-# Set PATH to include the directory where docker is located
-export PATH=$DOCKER_PATH:$GIT_PATH
-
-# Execute the "coretex node update" command
-. "$VENV_PATH/bin/activate" && which coretex && coretex node update -n
diff --git a/coretex/cli/main.py b/main.py
similarity index 58%
rename from coretex/cli/main.py
rename to main.py
index 283cde2e..167eca4f 100644
--- a/coretex/cli/main.py
+++ b/main.py
@@ -17,17 +17,19 @@
from importlib.metadata import version as getLibraryVersion
+import sys
+import multiprocessing
+
import click
-from .commands.login import login
-from .commands.model import model
-from .commands.node import node
-from .commands.task import task
-from .commands.project import project
+from coretex.cli.commands.login import login
+from coretex.cli.commands.model import model
+from coretex.cli.commands.node import node
+from coretex.cli.commands.task import task
+from coretex.cli.commands.project import project
-from .modules import ui, utils
-from .modules.intercept import ClickExceptionInterceptor
-from ..utils.process import CommandException
+from coretex.cli.modules import ui, utils
+from coretex.cli.modules.intercept import ClickExceptionInterceptor
@click.command()
@@ -38,24 +40,10 @@ def version() -> None:
@click.command()
def update() -> None:
- currentVersion = utils.fetchCurrentVersion()
- latestVersion = utils.fetchLatestVersion()
-
- if currentVersion is None or latestVersion is None:
- return
-
- if latestVersion > currentVersion:
- try:
- ui.progressEcho("Updating coretex...")
- utils.updateLib()
- ui.successEcho(
- f"Coretex successfully updated from {utils.formatCliVersion(currentVersion)} "
- f"to {utils.formatCliVersion(latestVersion)}!"
- )
- except CommandException:
- ui.errorEcho("Failed to update coretex.")
- else:
- ui.stdEcho("Coretex version is up to date.")
+ ui.stdEcho(
+ "This command isn't implemented yet for compiled version but will be available soon."
+ "Thanks for your patience!"
+ )
@click.group(cls = ClickExceptionInterceptor)
@@ -71,3 +59,14 @@ def cli() -> None:
cli.add_command(task)
cli.add_command(version)
cli.add_command(update)
+
+
+if __name__ == "__main__":
+ # Ensures compatibility with the multiprocessing module when running as a compiled executable.
+ # PyInstaller creates a 'frozen' executable, which might not handle multiprocessing properly without this.
+ multiprocessing.freeze_support()
+
+ # If the script is running as a PyInstaller compiled executable ('frozen' state),
+ # invoke the command-line interface (CLI) with the provided arguments.
+ if getattr(sys, 'frozen', False):
+ cli(sys.argv[1:])
diff --git a/pyproject.toml b/pyproject.toml
index 66bd80a0..52fb5202 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -56,6 +56,3 @@ exclude = ["tests*", "docs_images*", "doxygen-awesome-css*"]
[tool.setuptools.package-data]
"*" = ["py.typed", "resources/*"]
-
-[project.scripts]
-coretex = "coretex.cli.main:cli"