Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.
Merged
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
2 changes: 1 addition & 1 deletion engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ if(DEFINED CMAKE_JS_INC)
# define NPI_VERSION
add_compile_definitions(NAPI_VERSION=8)
endif()

add_compile_definitions(CORTEX_VARIANT="${CORTEX_VARIANT}")
add_compile_definitions(CORTEX_CPP_VERSION="${CORTEX_CPP_VERSION}")
add_compile_definitions(CORTEX_CONFIG_FILE_PATH="${CORTEX_CONFIG_FILE_PATH}")
Expand Down
145 changes: 86 additions & 59 deletions engine/commands/cortex_upd_cmd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "cortex_upd_cmd.h"
#include "httplib.h"
#include "nlohmann/json.hpp"
#include "server_stop_cmd.h"
#include "services/download_service.h"
#include "utils/archive_utils.h"
#include "utils/file_manager_utils.h"
Expand All @@ -12,21 +13,38 @@

namespace commands {

namespace {
const std::string kCortexBinary = "cortex-cpp";
}

CortexUpdCmd::CortexUpdCmd() {}

void CortexUpdCmd::Exec(std::string v) {
// TODO(sang) stop server if it is running
{
auto config = file_manager_utils::GetCortexConfig();
httplib::Client cli(config.apiServerHost + ":" + config.apiServerPort);
auto res = cli.Get("/health/healthz");
if (res) {
CLI_LOG("Server is running. Stopping server before updating!");
commands::ServerStopCmd ssc(config.apiServerHost,
std::stoi(config.apiServerPort));
ssc.Exec();
}
}
if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) {
if (!GetNightly(v))
return;
} else {
if (!GetStableAndBeta(v))
return;
}
CLI_LOG("Update cortex sucessfully");
}

bool CortexUpdCmd::GetStableAndBeta(const std::string& v) {
// Check if the architecture and OS are supported
auto system_info = system_info_utils::GetSystemInfo();
if (system_info.arch == system_info_utils::kUnsupported ||
system_info.os == system_info_utils::kUnsupported) {
CTL_ERR("Unsupported OS or architecture: " << system_info.os << ", "
<< system_info.arch);
return;
return false;
}
CTL_INF("OS: " << system_info.os << ", Arch: " << system_info.arch);

Expand All @@ -50,7 +68,7 @@ void CortexUpdCmd::Exec(std::string v) {
std::string matched_variant = "";
for (auto& asset : assets) {
auto asset_name = asset["name"].get<std::string>();
if (asset_name.find("cortex-cpp") != std::string::npos &&
if (asset_name.find(kCortexBinary) != std::string::npos &&
asset_name.find(os_arch) != std::string::npos) {
matched_variant = asset_name;
break;
Expand All @@ -59,7 +77,7 @@ void CortexUpdCmd::Exec(std::string v) {
}
if (matched_variant.empty()) {
CTL_ERR("No variant found for " << os_arch);
return;
return false;
}
CTL_INF("Matched variant: " << matched_variant);

Expand Down Expand Up @@ -99,73 +117,82 @@ void CortexUpdCmd::Exec(std::string v) {
archive_utils::ExtractArchive(download_path.string(),
extract_path.string());

// remove the downloaded file
// TODO(any) Could not delete file on Windows because it is currently hold by httplib(?)
// Not sure about other platforms
try {
std::filesystem::remove(absolute_path);
} catch (const std::exception& e) {
CTL_WRN("Could not delete file: " << e.what());
}
CTL_INF("Finished!");
});
break;
}
}
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error: " << e.what() << std::endl;
return;
return false;
}
} else {
CTL_ERR("HTTP error: " << res->status);
return;
return false;
}
} else {
auto err = res.error();
CTL_ERR("HTTP error: " << httplib::to_string(err));
return;
return false;
}
#if defined(_WIN32)
auto executable_path = file_manager_utils::GetExecutableFolderContainerPath();
auto temp = executable_path / "cortex_tmp.exe";
remove(temp.string().c_str()); // ignore return code

auto src =
executable_path / "cortex" / kCortexBinary / (kCortexBinary + ".exe");
auto dst = executable_path / (kCortexBinary + ".exe");
// Rename
rename(dst.string().c_str(), temp.string().c_str());
// Update
CopyFile(const_cast<char*>(src.string().c_str()),
const_cast<char*>(dst.string().c_str()), false);
auto download_folder = executable_path / "cortex";
remove(download_folder);
remove(temp.string().c_str());
#else

// Replace binary file
auto executable_path = file_manager_utils::GetExecutableFolderContainerPath();
auto temp = executable_path / "cortex_tmp";
auto src = executable_path / "cortex" / kCortexBinary / kCortexBinary;
auto dst = executable_path / kCortexBinary;
if (std::rename(dst.string().c_str(), temp.string().c_str())) {
CTL_ERR("Failed to rename from " << dst.string() << " to "
<< temp.string());
return;
}
try {
std::filesystem::copy_file(
src, dst, std::filesystem::copy_options::overwrite_existing);
std::filesystem::permissions(dst, std::filesystem::perms::owner_all |
std::filesystem::perms::group_all |
std::filesystem::perms::others_read |
std::filesystem::perms::others_exec);
std::filesystem::remove(temp);
auto download_folder = executable_path / "cortex/";
std::filesystem::remove_all(download_folder);
} catch (const std::exception& e) {
CTL_WRN("Something wrong happened: " << e.what());
return;
auto src = executable_path / "cortex" / kCortexBinary / GetCortexBinary();
auto dst = executable_path / GetCortexBinary();
return ReplaceBinaryInflight(src, dst);
}

bool CortexUpdCmd::GetNightly(const std::string& v) {
// Check if the architecture and OS are supported
auto system_info = system_info_utils::GetSystemInfo();
if (system_info.arch == system_info_utils::kUnsupported ||
system_info.os == system_info_utils::kUnsupported) {
CTL_ERR("Unsupported OS or architecture: " << system_info.os << ", "
<< system_info.arch);
return false;
}
#endif
CLI_LOG("Update cortex sucessfully");
CTL_INF("OS: " << system_info.os << ", Arch: " << system_info.arch);

// Download file
std::string version = v.empty() ? "latest" : std::move(v);
std::ostringstream release_path;
release_path << "cortex/" << version << "/" << system_info.os << "-"
<< system_info.arch << "/" << kNightlyFileName;
CTL_INF("Engine release path: " << kNightlyHost << release_path.str());

auto download_task = DownloadTask{.id = "cortex",
.type = DownloadType::Cortex,
.error = std::nullopt,
.items = {DownloadItem{
.id = "cortex",
.host = kNightlyHost,
.fileName = kNightlyFileName,
.type = DownloadType::Cortex,
.path = release_path.str(),
}}};

DownloadService download_service;
download_service.AddDownloadTask(
download_task, [this](const std::string& absolute_path, bool unused) {
// try to unzip the downloaded file
std::filesystem::path download_path{absolute_path};
CTL_INF("Downloaded engine path: " << download_path.string());

std::filesystem::path extract_path =
download_path.parent_path().parent_path();

archive_utils::ExtractArchive(download_path.string(),
extract_path.string());

CTL_INF("Finished!");
});

// Replace binay file
auto executable_path = file_manager_utils::GetExecutableFolderContainerPath();
auto src = executable_path / "cortex" / GetCortexBinary();
auto dst = executable_path / GetCortexBinary();
return ReplaceBinaryInflight(src, dst);
}

} // namespace commands
115 changes: 113 additions & 2 deletions engine/commands/cortex_upd_cmd.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,124 @@
#pragma once
#include <string>
#include <optional>
#include <string>

#include "httplib.h"
#include "nlohmann/json.hpp"
#include "utils/file_manager_utils.h"
#include "utils/logging_utils.h"

namespace commands {
#ifndef CORTEX_VARIANT
#define CORTEX_VARIANT file_manager_utils::kProdVariant
#endif
constexpr const auto kNightlyHost = "https://delta.jan.ai";
constexpr const auto kNightlyFileName = "cortex-nightly.tar.gz";
const std::string kCortexBinary = "cortex";

inline std::string GetCortexBinary() {
#if defined(_WIN32)
constexpr const bool has_exe = true;
#else
constexpr const bool has_exe = false;
#endif
if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) {
return has_exe ? kCortexBinary + "-nightly.exe"
: kCortexBinary + "-nightly";
} else if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) {
return has_exe ? kCortexBinary + "-beta.exe" : kCortexBinary + "-beta";
} else {
return has_exe ? kCortexBinary + ".exe" : kCortexBinary;
}
}

inline std::string GetHostName() {
if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) {
return "https://delta.jan.ai";
} else {
return "https://api.github.com";
}
}

inline std::string GetReleasePath() {
if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) {
return "/cortex/latest/version.json";
} else {
return "/repos/janhq/cortex.cpp/releases/latest";
}
}

inline void CheckNewUpdate() {
auto host_name = GetHostName();
auto release_path = GetReleasePath();
CTL_INF("Engine release path: " << host_name << release_path);

class CortexUpdCmd{
httplib::Client cli(host_name);
if (auto res = cli.Get(release_path)) {
if (res->status == httplib::StatusCode::OK_200) {
try {
auto json_res = nlohmann::json::parse(res->body);
std::string latest_version = json_res["tag_name"].get<std::string>();
std::string current_version = CORTEX_CPP_VERSION;
if (current_version != latest_version) {
CLI_LOG("\nA new release of cortex is available: "
<< current_version << " -> " << latest_version);
CLI_LOG("To upgrade, run: cortex update");
// CLI_LOG(json_res["html_url"].get<std::string>());
}
} catch (const nlohmann::json::parse_error& e) {
CTL_INF("JSON parse error: " << e.what());
}
} else {
CTL_INF("HTTP error: " << res->status);
}
} else {
auto err = res.error();
CTL_INF("HTTP error: " << httplib::to_string(err));
}
}

inline bool ReplaceBinaryInflight(const std::filesystem::path& src,
const std::filesystem::path& dst) {
if (src == dst) {
// Already has the newest
return true;
}
std::filesystem::path temp = std::filesystem::temp_directory_path() / "cortex_temp";

try {
if (std::filesystem::exists(temp)) {
std::filesystem::remove(temp);
}

std::rename(dst.string().c_str(), temp.string().c_str());
std::filesystem::copy_file(
src, dst, std::filesystem::copy_options::overwrite_existing);
std::filesystem::permissions(dst, std::filesystem::perms::owner_all |
std::filesystem::perms::group_all |
std::filesystem::perms::others_read |
std::filesystem::perms::others_exec);
auto download_folder = src.parent_path();
std::filesystem::remove_all(download_folder);
} catch (const std::exception& e) {
CTL_ERR("Something wrong happened: " << e.what());
if (std::filesystem::exists(temp)) {
std::rename(temp.string().c_str(), dst.string().c_str());
CLI_LOG("Restored binary file");
}
return false;
}

return true;
}

class CortexUpdCmd {
public:
CortexUpdCmd();
void Exec(std::string version);

private:
bool GetStableAndBeta(const std::string& v);
bool GetNightly(const std::string& v);
};

} // namespace commands
Loading