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 1 commit
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
139 changes: 80 additions & 59 deletions engine/commands/cortex_upd_cmd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,35 @@
#include "utils/file_manager_utils.h"
#include "utils/logging_utils.h"
#include "utils/system_info_utils.h"
#include "server_stop_cmd.h"

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
// {
// commands::ServerStopCmd ssc("127.0.0.1", 3928);
// ssc.Exec();
// }
if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) {
if (!GetNightly(v))
return;
} else {
if (!GetProAndBeta(v))
return;
}
CLI_LOG("Update cortex sucessfully");
}

bool CortexUpdCmd::GetProAndBeta(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 +61,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 +70,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 +110,83 @@ 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 binay 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" / "cortex-cpp" / 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
116 changes: 114 additions & 2 deletions engine/commands/cortex_upd_cmd.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,125 @@
#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 = dst.parent_path() / "cortex_temp";
std::cout << temp.string() << std::endl;

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 GetProAndBeta(const std::string& v);
bool GetNightly(const std::string& v);
};

} // namespace commands
29 changes: 1 addition & 28 deletions engine/controllers/command_line_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,34 +193,7 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) {
// Check new update, only check for stable release for now
#ifdef CORTEX_CPP_VERSION
if (check_update) {
constexpr auto github_host = "https://api.github.com";
std::ostringstream release_path;
release_path << "/repos/janhq/cortex.cpp/releases/latest";
CTL_INF("Engine release path: " << github_host << release_path.str());

httplib::Client cli(github_host);
if (auto res = cli.Get(release_path.str())) {
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));
}
commands::CheckNewUpdate();
}
#endif

Expand Down
1 change: 1 addition & 0 deletions engine/services/download_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ void DownloadService::StartDownloadItem(
}
if (current == total) {
outputFile.flush();
outputFile.close();
CLI_LOG("Done download: " << static_cast<double>(total) / 1024 / 1024
<< " MiB");
if (callback.has_value()) {
Expand Down