This commit is contained in:
PJarczak 2026-04-14 19:17:34 +02:00
parent 9a78a706fb
commit 836be36a48
26 changed files with 924 additions and 604 deletions

View file

@ -146,49 +146,19 @@ jobs:
run: .\build_release_vs.bat slicer
- name: Ensure WSL is available for runtime validation
- name: Note about hosted-runner WSL validation
shell: pwsh
run: |
$wsl = Join-Path $env:WINDIR 'System32\wsl.exe'
if (!(Test-Path $wsl)) { throw 'wsl.exe not found' }
$ready = $false
try {
& $wsl --status
if ($LASTEXITCODE -eq 0) { $ready = $true }
} catch {}
if (-not $ready) {
& $wsl --install --no-distribution
if ($LASTEXITCODE -ne 0) { throw 'wsl --install --no-distribution failed' }
}
& $wsl --update
if ($LASTEXITCODE -ne 0) { throw 'wsl --update failed' }
& $wsl --status
if ($LASTEXITCODE -ne 0) { throw 'WSL is still not ready after install/update' }
Write-Host 'Hosted GitHub Windows runners are packaging-only for this bridge.'
Write-Host 'Real WSL validation must run on a self-hosted Windows machine or on the end user machine at first launch.'
Write-Host 'The bundled install_runtime.ps1 still performs first-run install and repair.'
- name: Show packaged Orca runtime files
shell: pwsh
run: |
Get-ChildItem -Force "$env:GITHUB_WORKSPACE\build\OrcaSlicer" | Format-Table Name, Length -AutoSize
- name: Install bundled WSL2 Orca runtime into live plugins dir
shell: pwsh
run: |
& "$env:GITHUB_WORKSPACE\build\OrcaSlicer\install_runtime.ps1" -ReplaceExisting
- name: Show live Orca plugin dir after installer
shell: pwsh
run: |
Get-ChildItem -Force "$env:APPDATA\OrcaSlicer\plugins" | Format-Table Name, Length -AutoSize
- name: Validate live Orca plugin dir bootstrap package
shell: pwsh
run: |
& "$env:APPDATA\OrcaSlicer\plugins\verify_runtime.ps1" -PackageDir "$env:APPDATA\OrcaSlicer\plugins" -PluginCacheDir "$env:APPDATA\OrcaSlicer\plugins" -AllowMissingLinuxPlugin
- name: Create installer Win
working-directory: ${{ github.workspace }}/build
shell: pwsh

View file

@ -0,0 +1,56 @@
#pragma once
#include <string>
#include <nlohmann/json.hpp>
namespace Slic3r::BambuBridge {
inline bool looks_like_change_user_data(const nlohmann::json& j)
{
if (!j.is_object())
return false;
return j.contains("token") ||
j.contains("access_token") ||
j.contains("refresh_token") ||
j.contains("code") ||
j.contains("user") ||
j.contains("user_id") ||
j.contains("uidStr");
}
inline nlohmann::json normalize_change_user_payload(const nlohmann::json& input)
{
if (!input.is_object())
return input;
const auto command = input.value("command", std::string());
if ((command == "user_login" || command == "login_token") && input.contains("data") && input["data"].is_object())
return input;
auto data_it = input.find("data");
if (data_it != input.end() && looks_like_change_user_data(*data_it))
return {
{"command", command.empty() ? "user_login" : command},
{"data", *data_it},
};
if (looks_like_change_user_data(input))
return {
{"command", "user_login"},
{"data", input},
};
return input;
}
inline std::string normalize_change_user_payload_string(const std::string& input)
{
try {
const auto parsed = nlohmann::json::parse(input);
return normalize_change_user_payload(parsed).dump();
} catch (...) {
return input;
}
}
}

View file

@ -0,0 +1,9 @@
#pragma once
#include "../BambuBridge/Auth/BridgeAuthPayload.hpp"
namespace Slic3r::PJarczakLinuxBridge {
using Slic3r::BambuBridge::looks_like_change_user_data;
using Slic3r::BambuBridge::normalize_change_user_payload;
using Slic3r::BambuBridge::normalize_change_user_payload_string;
}

View file

@ -57,6 +57,9 @@
#include <wx/fontutil.h>
#include <wx/glcanvas.h>
#include <wx/utils.h>
#ifdef __WXMSW__
#include <windows.h>
#endif
#include <openssl/hmac.h>
#include <openssl/evp.h>
@ -3169,6 +3172,79 @@ static const char* pjarczak_legacy_bootstrap_script_name()
return "pjarczak-wsl-run-host.sh";
}
static bool pjarczak_run_hidden_sync(const wxString& command, std::string* combined_output)
{
wxArrayString output;
wxArrayString errors;
const long rc = wxExecute(command, output, errors, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE);
std::ostringstream oss;
for (const auto& line : output)
oss << into_u8(line) << "\n";
for (const auto& line : errors)
oss << into_u8(line) << "\n";
if (combined_output)
*combined_output = oss.str();
return rc == 0;
}
static void pjarczak_log_multiline(const char* prefix, const std::string& text)
{
std::istringstream iss(text);
std::string line;
while (std::getline(iss, line)) {
if (!line.empty())
BOOST_LOG_TRIVIAL(info) << prefix << line;
}
}
static bool pjarczak_ensure_windows_wsl_runtime_ready(const boost::filesystem::path& plugin_folder)
{
#ifndef WIN32
(void)plugin_folder;
return true;
#else
static bool attempted = false;
if (attempted)
return true;
attempted = true;
const auto verify_script = plugin_folder / Slic3r::PJarczakLinuxBridge::windows_wsl_validate_script_file_name();
const auto install_cmd = plugin_folder / "install_runtime.cmd";
const auto cache_dir = boost::filesystem::path(wxGetApp().data_dir()) / "ota";
if (!boost::filesystem::exists(verify_script) || !boost::filesystem::exists(install_cmd)) {
BOOST_LOG_TRIVIAL(warning) << "[pjarczak_wsl] missing runtime verification or installer script in " << plugin_folder.string();
return false;
}
std::string verify_output;
const wxString verify_cmd = wxString::Format(
"powershell -NoProfile -ExecutionPolicy Bypass -File \"%s\" -PackageDir \"%s\" -PluginCacheDir \"%s\" -AllowMissingLinuxPlugin",
from_u8(verify_script.string()),
from_u8(plugin_folder.string()),
from_u8(cache_dir.string()));
if (pjarczak_run_hidden_sync(verify_cmd, &verify_output)) {
pjarczak_log_multiline("[pjarczak_wsl][verify] ", verify_output);
return true;
}
BOOST_LOG_TRIVIAL(warning) << "[pjarczak_wsl] verify_runtime.ps1 failed, running installer/repair";
pjarczak_log_multiline("[pjarczak_wsl][verify] ", verify_output);
std::string install_output;
const wxString install_cmdline = wxString::Format(
"cmd.exe /S /C \"\"%s\" -PackageDir \"%s\" -PluginDir \"%s\"\"",
from_u8(install_cmd.string()),
from_u8(plugin_folder.string()),
from_u8(plugin_folder.string()));
const bool install_ok = pjarczak_run_hidden_sync(install_cmdline, &install_output);
pjarczak_log_multiline("[pjarczak_wsl][install] ", install_output);
return install_ok;
#endif
}
bool pjarczak_bridge_payload_ready(const boost::filesystem::path& plugin_folder, std::string* reason)
{
if (!Slic3r::PJarczakLinuxBridge::enabled()) {
@ -3184,6 +3260,8 @@ bool pjarczak_bridge_payload_ready(const boost::filesystem::path& plugin_folder,
const std::string required_files[] = {
Slic3r::PJarczakLinuxBridge::bridge_network_current_dir_name(),
Slic3r::PJarczakLinuxBridge::host_executable_file_name(),
Slic3r::PJarczakLinuxBridge::host_executable_abi1_file_name(),
Slic3r::PJarczakLinuxBridge::host_executable_abi0_file_name(),
Slic3r::PJarczakLinuxBridge::windows_wsl_distro_file_name(),
Slic3r::PJarczakLinuxBridge::windows_wsl_validate_script_file_name(),
Slic3r::PJarczakLinuxBridge::windows_wsl_rootfs_file_name(),
@ -3289,6 +3367,8 @@ void pjarczak_copy_local_overlay_runtime(const boost::filesystem::path& plugin_f
const std::string runtime_files[] = {
Slic3r::PJarczakLinuxBridge::bridge_network_current_dir_name(),
Slic3r::PJarczakLinuxBridge::host_executable_file_name(),
Slic3r::PJarczakLinuxBridge::host_executable_abi1_file_name(),
Slic3r::PJarczakLinuxBridge::host_executable_abi0_file_name(),
Slic3r::PJarczakLinuxBridge::windows_wsl_distro_file_name(),
Slic3r::PJarczakLinuxBridge::windows_wsl_import_script_file_name(),
Slic3r::PJarczakLinuxBridge::windows_wsl_validate_script_file_name(),
@ -3497,6 +3577,10 @@ bool GUI_App::on_init_network(bool try_backup)
auto should_load_networking_plugin = app_config->get_bool("installed_networking");
const bool bridge_mode = Slic3r::PJarczakLinuxBridge::enabled();
const boost::filesystem::path bridge_plugin_folder = boost::filesystem::path(data_dir()) / "plugins";
if (bridge_mode) {
pjarczak_copy_local_overlay_runtime(bridge_plugin_folder);
pjarczak_ensure_windows_wsl_runtime_ready(bridge_plugin_folder);
}
std::string bridge_payload_reason;
const bool bridge_payload_ready = !bridge_mode || pjarczak_bridge_payload_ready(bridge_plugin_folder, &bridge_payload_reason);
bool create_network_agent = false;

View file

@ -182,14 +182,76 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
{
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: get_response";
NetworkAgent* agent = wxGetApp().getAgent();
if (!agent)
return std::make_shared<ResponseNotFound>();
const std::string ticket = url_get_param(url, "ticket");
if (!ticket.empty()) {
unsigned int token_http_code = 0;
std::string token_http_body;
const int token_result = agent->get_my_token(ticket, &token_http_code, &token_http_body);
BOOST_LOG_TRIVIAL(info) << "thirdparty_login ticket exchange result=" << token_result << ", http_code=" << token_http_code;
if (token_result == 0) {
std::string access_token;
try {
json token_j = json::parse(token_http_body);
if (token_j.contains("access_token"))
access_token = token_j["access_token"].get<std::string>();
} catch (...) {}
unsigned int profile_http_code = 0;
std::string profile_http_body;
const int profile_result = access_token.empty() ? -1 : agent->get_my_profile(access_token, &profile_http_code, &profile_http_body);
BOOST_LOG_TRIVIAL(info) << "thirdparty_login profile result=" << profile_result << ", http_code=" << profile_http_code;
if (profile_result == 0) {
json payload;
payload["command"] = "user_login";
try {
payload["data"] = json::parse(token_http_body);
} catch (...) {
payload["data"]["token"] = access_token;
}
try {
const auto profile_j = json::parse(profile_http_body);
payload["data"]["user"] = profile_j;
} catch (...) {}
agent->change_user(payload.dump());
const bool login_ok = agent->is_user_login();
if (login_ok) {
wxGetApp().request_user_login(1);
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
}
const std::string title = login_ok ? "Authentication complete" : "Authentication failed";
const std::string message = login_ok
? "You can return to OrcaSlicer. This window will close automatically."
: "Something went wrong. Please return to OrcaSlicer and try again.";
const std::string html =
"<html><head><meta charset=\"utf-8\">"
"<style>body{font-family:Arial,sans-serif;background:#f7f7f7;color:#222;margin:32px;}"
"a.button{display:inline-block;padding:10px 16px;margin-top:12px;background:#0f8bff;color:#fff;text-decoration:none;border-radius:6px;}"
"</style></head><body><div class=\"container\">"
"<h2>" + title + "</h2>"
"<p>" + message + "</p>"
"<script>setTimeout(function(){try{window.close();}catch(e){}},1500);</script>"
"</div></body></html>";
return std::make_shared<ResponseHtml>(html);
}
}
const std::string html =
"<html><head><meta charset=\"utf-8\"></head><body>"
"<h2>Authentication failed</h2>"
"<p>Something went wrong. Please return to OrcaSlicer and try again.</p>"
"</body></html>";
return std::make_shared<ResponseHtml>(html);
}
const std::string auth_code = url_get_param(url, "code");
if (!auth_code.empty()) {
std::string state = url_get_param(url, "state");
NetworkAgent* agent = wxGetApp().getAgent();
if (!agent) {
return std::make_shared<ResponseNotFound>();
}
json payload;
payload["command"] = "user_login";
payload["data"]["code"] = auth_code;
@ -224,7 +286,6 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
std::string refresh_token = url_get_param(url, "refresh_token");
std::string expires_in_str = url_get_param(url, "expires_in");
std::string refresh_expires_in_str = url_get_param(url, "refresh_expires_in");
NetworkAgent* agent = wxGetApp().getAgent();
unsigned int http_code;
std::string http_body;
@ -244,10 +305,9 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
user_avatar = user_j["avatar"].get<std::string>();
if (user_j.contains("account"))
user_account = user_j["account"].get<std::string>();
} catch (...) {
;
}
} catch (...) {}
json j;
j["command"] = "user_login";
j["data"]["refresh_token"] = refresh_token;
j["data"]["token"] = access_token;
j["data"]["expires_in"] = expires_in_str;
@ -263,14 +323,14 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
std::string location_str = (boost::format("%1%?result=success") % redirect_url).str();
return std::make_shared<ResponseRedirect>(location_str);
} else {
std::string error_str = "get_user_profile_error_" + std::to_string(result);
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % redirect_url % error_str).str();
return std::make_shared<ResponseRedirect>(location_str);
}
} else {
return std::make_shared<ResponseNotFound>();
std::string error_str = "get_user_profile_error_" + std::to_string(result);
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % redirect_url % error_str).str();
return std::make_shared<ResponseRedirect>(location_str);
}
return std::make_shared<ResponseNotFound>();
}
void HttpServer::ResponseNotFound::write_response(std::stringstream& ssOut)

View file

@ -6,6 +6,8 @@
#include "slic3r/GUI/wxExtensions.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "libslic3r_version.h"
#include "slic3r/Utils/PJarczakLinuxBridge/PJarczakLinuxBridgeConfig.hpp"
#include "slic3r/Utils/Http.hpp"
#include <wx/sizer.h>
#include <wx/toolbar.h>
@ -32,6 +34,23 @@ using namespace nlohmann;
namespace Slic3r { namespace GUI {
namespace {
std::string pjarczak_browser_login_url(const std::string& host_value, const std::string& locale, const std::string& localhost_base)
{
std::string host = host_value;
while (!host.empty() && host.back() == '/')
host.pop_back();
if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0)
host = "https://bambulab.com";
std::string lang = locale.empty() ? "en" : locale;
std::string callback = host + "/sign-in/callback?source=portal&locale=" + Http::url_encode(lang) +
"&redirect_url=" + Http::url_encode(localhost_base) +
"&openBy=suite&from=studio&slicerLoginType=ticket";
return host + "/sign-in?&from=studio&source=portal&to=" + Http::url_encode(callback);
}
}
#define NETWORK_OFFLINE_TIMER_ID 10001
BEGIN_EVENT_TABLE(ZUserLogin, wxDialog)
@ -76,6 +95,20 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
CentreOnParent();
}
else {
#ifdef WIN32
if (Slic3r::PJarczakLinuxBridge::enabled()) {
m_external_browser_mode = true;
SetTitle(_L("Login"));
wxBoxSizer* m_sizer_main = new wxBoxSizer(wxVERTICAL);
auto* m_message = new wxStaticText(this, wxID_ANY, _L("Login opens in your default browser. Finish sign-in there and this dialog will close automatically."), wxDefaultPosition, wxDefaultSize, 0);
m_message->Wrap(FromDIP(420));
m_sizer_main->Add(m_message, 0, wxALL | wxEXPAND, FromDIP(16));
SetSizer(m_sizer_main);
m_sizer_main->SetSizeHints(this);
SetSize(FromDIP(wxSize(460, 140)));
CentreOnParent();
} else {
#endif
// Get the login URL from the cloud service agent
wxString strlang = wxGetApp().current_language_code_safe();
strlang.Replace("_", "-");
@ -128,6 +161,9 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
Move(tmpPT);
#ifdef WIN32
}
#endif
}
wxGetApp().UpdateDlgDarkUI(this);
}
@ -151,7 +187,25 @@ void ZUserLogin::OnTimer(wxTimerEvent &event) {
bool ZUserLogin::run() {
m_timer = new wxTimer(this, NETWORK_OFFLINE_TIMER_ID);
m_timer->Start(8000);
m_timer->Start(m_external_browser_mode ? 30000 : 8000);
#ifdef WIN32
if (m_external_browser_mode) {
NetworkAgent* agent = wxGetApp().getAgent();
if (agent) {
wxString strlang = wxGetApp().current_language_code_safe();
strlang.Replace("_", "-");
const std::string host = agent->get_cloud_service_host();
m_loopback_port = LOCALHOST_PORT;
wxGetApp().start_http_server(m_loopback_port);
const std::string localhost = std::string(LOCALHOST_URL) + std::to_string(m_loopback_port);
const std::string browser_url = pjarczak_browser_login_url(host, strlang.ToStdString(), localhost);
BOOST_LOG_TRIVIAL(info) << "external login url = " << browser_url;
wxLaunchDefaultBrowser(wxString::FromUTF8(browser_url));
m_networkOk = true;
}
}
#endif
if (this->ShowModal() == wxID_OK) {
return true;

View file

@ -72,6 +72,7 @@ private:
std::string m_AutotestToken;
int m_loopback_port { 0 };
bool m_external_browser_mode { false };
#if wxUSE_WEBVIEW_IE
wxMenuItem *m_script_object_el;

View file

@ -617,6 +617,17 @@ int BBLCloudServiceAgent::get_model_mall_detail_url(std::string* url, std::strin
return -1;
}
int BBLCloudServiceAgent::get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_my_token();
if (func && agent) {
return func(agent, ticket, http_code, http_body);
}
return -1;
}
int BBLCloudServiceAgent::get_my_profile(std::string token, unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();

View file

@ -97,6 +97,7 @@ public:
int get_subtask(BBLModelTask* task, OnGetSubTaskFn getsub_fn) override;
int get_model_mall_home_url(std::string* url) override;
int get_model_mall_detail_url(std::string* url, std::string id) override;
int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body) override;
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) override;
// Analytics & Tracking

View file

@ -634,6 +634,7 @@ void BBLNetworkPlugin::load_all_function_pointers()
m_get_subtask = reinterpret_cast<func_get_subtask>(get_function("bambu_network_get_subtask"));
m_get_model_mall_home_url = reinterpret_cast<func_get_model_mall_home_url>(get_function("bambu_network_get_model_mall_home_url"));
m_get_model_mall_detail_url = reinterpret_cast<func_get_model_mall_detail_url>(get_function("bambu_network_get_model_mall_detail_url"));
m_get_my_token = reinterpret_cast<func_get_my_token>(get_function("bambu_network_get_my_token"));
m_get_my_profile = reinterpret_cast<func_get_my_profile>(get_function("bambu_network_get_my_profile"));
m_track_enable = reinterpret_cast<func_track_enable>(get_function("bambu_network_track_enable"));
m_track_remove_files = reinterpret_cast<func_track_remove_files>(get_function("bambu_network_track_remove_files"));
@ -737,6 +738,7 @@ void BBLNetworkPlugin::clear_all_function_pointers()
m_get_subtask = nullptr;
m_get_model_mall_home_url = nullptr;
m_get_model_mall_detail_url = nullptr;
m_get_my_token = nullptr;
m_get_my_profile = nullptr;
m_track_enable = nullptr;
m_track_remove_files = nullptr;

View file

@ -106,6 +106,7 @@ typedef int (*func_get_model_publish_url)(void *agent, std::string* url);
typedef int (*func_get_subtask)(void *agent, BBLModelTask* task, OnGetSubTaskFn getsub_fn);
typedef int (*func_get_model_mall_home_url)(void *agent, std::string* url);
typedef int (*func_get_model_mall_detail_url)(void *agent, std::string* url, std::string id);
typedef int (*func_get_my_token)(void *agent, std::string ticket, unsigned int *http_code, std::string *http_body);
typedef int (*func_get_my_profile)(void *agent, std::string token, unsigned int *http_code, std::string *http_body);
typedef int (*func_track_enable)(void *agent, bool enable);
typedef int (*func_track_remove_files)(void *agent);
@ -360,6 +361,7 @@ public:
func_get_subtask get_get_subtask() const { return m_get_subtask; }
func_get_model_mall_home_url get_get_model_mall_home_url() const { return m_get_model_mall_home_url; }
func_get_model_mall_detail_url get_get_model_mall_detail_url() const { return m_get_model_mall_detail_url; }
func_get_my_token get_get_my_token() const { return m_get_my_token; }
func_get_my_profile get_get_my_profile() const { return m_get_my_profile; }
func_track_enable get_track_enable() const { return m_track_enable; }
func_track_remove_files get_track_remove_files() const { return m_track_remove_files; }
@ -495,6 +497,7 @@ private:
func_get_subtask m_get_subtask{nullptr};
func_get_model_mall_home_url m_get_model_mall_home_url{nullptr};
func_get_model_mall_detail_url m_get_model_mall_detail_url{nullptr};
func_get_my_token m_get_my_token{nullptr};
func_get_my_profile m_get_my_profile{nullptr};
func_track_enable m_track_enable{nullptr};
func_track_remove_files m_track_remove_files{nullptr};

View file

@ -351,6 +351,7 @@ public:
/**
* Retrieve user's model mall profile.
*/
virtual int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body) = 0;
virtual int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) = 0;
// ========================================================================

View file

@ -775,6 +775,12 @@ int NetworkAgent::get_model_mall_detail_url(std::string* url, std::string id)
return -1;
}
int NetworkAgent::get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body)
{
if (m_cloud_agent) return m_cloud_agent->get_my_token(ticket, http_code, http_body);
return -1;
}
int NetworkAgent::get_my_profile(std::string token, unsigned int* http_code, std::string* http_body)
{
if (m_cloud_agent) return m_cloud_agent->get_my_profile(token, http_code, http_body);

View file

@ -143,6 +143,7 @@ public:
int get_subtask(BBLModelTask* task, OnGetSubTaskFn getsub_fn);
int get_model_mall_home_url(std::string* url);
int get_model_mall_detail_url(std::string* url, std::string id);
int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body);
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body);
int track_enable(bool enable);
int track_remove_files();

View file

@ -51,32 +51,41 @@ install(TARGETS pjarczak_bambu_networking_bridge
)
if (UNIX AND NOT APPLE AND EXISTS "${PJARCZAK_LINUX_HOST_DIR}/main.cpp" AND EXISTS "${PJARCZAK_LINUX_HOST_DIR}/LinuxPluginHost.cpp")
add_executable(pjarczak_bambu_linux_host
${PJARCZAK_LINUX_HOST_DIR}/main.cpp
${PJARCZAK_LINUX_HOST_DIR}/LinuxPluginHost.cpp
${CMAKE_CURRENT_LIST_DIR}/PJarczakLinuxBridgeConfig.cpp
${CMAKE_CURRENT_LIST_DIR}/PJarczakLinuxSoBridgeRpcProtocol.cpp
)
target_include_directories(pjarczak_bambu_linux_host PRIVATE ${PJARCZAK_LINUX_BRIDGE_INCLUDE_DIRS})
target_compile_definitions(pjarczak_bambu_linux_host PRIVATE
PJARCZAK_LINUX_BRIDGE_STANDALONE_HOST=1
PJARCZAK_LINUX_BRIDGE_LIGHTWEIGHT_TASKS=1
)
find_package(nlohmann_json CONFIG QUIET)
if (TARGET nlohmann_json::nlohmann_json)
target_link_libraries(pjarczak_bambu_linux_host PRIVATE nlohmann_json::nlohmann_json)
endif()
target_link_libraries(pjarczak_bambu_linux_host PRIVATE boost_headeronly boost_libs ${CMAKE_DL_LIBS} OpenSSL::Crypto)
target_link_options(pjarczak_bambu_linux_host PRIVATE -static-libstdc++ -static-libgcc)
set_target_properties(pjarczak_bambu_linux_host PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
)
install(TARGETS pjarczak_bambu_linux_host
RUNTIME DESTINATION "."
)
function(pjarczak_add_linux_host_variant target_name abi_value)
add_executable(${target_name}
${PJARCZAK_LINUX_HOST_DIR}/main.cpp
${PJARCZAK_LINUX_HOST_DIR}/LinuxPluginHost.cpp
${CMAKE_CURRENT_LIST_DIR}/PJarczakLinuxBridgeConfig.cpp
${CMAKE_CURRENT_LIST_DIR}/PJarczakLinuxSoBridgeRpcProtocol.cpp
)
target_include_directories(${target_name} PRIVATE ${PJARCZAK_LINUX_BRIDGE_INCLUDE_DIRS})
target_compile_definitions(${target_name} PRIVATE
PJARCZAK_LINUX_BRIDGE_STANDALONE_HOST=1
PJARCZAK_LINUX_BRIDGE_LIGHTWEIGHT_TASKS=1
_GLIBCXX_USE_CXX11_ABI=${abi_value}
)
find_package(nlohmann_json CONFIG QUIET)
if (TARGET nlohmann_json::nlohmann_json)
target_link_libraries(${target_name} PRIVATE nlohmann_json::nlohmann_json)
endif()
target_link_libraries(${target_name} PRIVATE boost_headeronly boost_libs ${CMAKE_DL_LIBS} OpenSSL::Crypto)
set_target_properties(${target_name} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH "$ORIGIN"
)
install(TARGETS ${target_name}
RUNTIME DESTINATION "."
)
endfunction()
pjarczak_add_linux_host_variant(pjarczak_bambu_linux_host_abi1 1)
pjarczak_add_linux_host_variant(pjarczak_bambu_linux_host_abi0 0)
add_custom_target(pjarczak_bambu_linux_host DEPENDS pjarczak_bambu_linux_host_abi1 pjarczak_bambu_linux_host_abi0)
endif()
if (APPLE)
set(PJARCZAK_MAC_WRAPPER_SCRIPT_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_macos_linux_wrapper/pjarczak_bambu_macos_linux_wrapper.sh)
add_custom_command(TARGET pjarczak_bambu_networking_bridge POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
@ -91,6 +100,7 @@ endif()
if (WIN32)
set(PJARCZAK_WSL_HELPER_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/wsl/pjarczak_wsl_run_host.sh)
set(PJARCZAK_WSL_HOST_WRAPPER_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/wsl/pjarczak_bambu_linux_host)
set(PJARCZAK_WSL_INSTALLER_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/wsl/install_runtime.ps1)
set(PJARCZAK_WSL_INSTALLER_CMD_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/wsl/install_runtime.cmd)
set(PJARCZAK_WSL_VERIFY_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/wsl/verify_runtime.ps1)
@ -100,6 +110,9 @@ if (WIN32)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_WSL_HELPER_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/pjarczak_wsl_run_host.sh
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_WSL_HOST_WRAPPER_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/pjarczak_bambu_linux_host
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_WSL_INSTALLER_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/install_runtime.ps1
@ -118,6 +131,7 @@ if (WIN32)
)
install(FILES
${PJARCZAK_WSL_HELPER_SOURCE}
${PJARCZAK_WSL_HOST_WRAPPER_SOURCE}
${PJARCZAK_WSL_INSTALLER_SOURCE}
${PJARCZAK_WSL_INSTALLER_CMD_SOURCE}
${PJARCZAK_WSL_VERIFY_SOURCE}

View file

@ -182,6 +182,16 @@ std::string host_executable_file_name()
return "pjarczak_bambu_linux_host";
}
std::string host_executable_abi1_file_name()
{
return "pjarczak_bambu_linux_host_abi1";
}
std::string host_executable_abi0_file_name()
{
return "pjarczak_bambu_linux_host_abi0";
}
std::string mac_host_wrapper_file_name()
{
return "pjarczak-bambu-linux-host-wrapper";
@ -231,12 +241,15 @@ bool is_overlay_runtime_filename(const std::string& file_name)
return file_name == linux_payload_manifest_file_name() ||
file_name == bridge_network_current_dir_name() ||
file_name == host_executable_file_name() ||
file_name == host_executable_abi1_file_name() ||
file_name == host_executable_abi0_file_name() ||
file_name == mac_host_wrapper_file_name() ||
file_name == windows_wsl_distro_file_name() ||
file_name == windows_wsl_import_script_file_name() ||
file_name == windows_wsl_validate_script_file_name() ||
file_name == windows_wsl_bootstrap_script_file_name() ||
file_name == windows_wsl_rootfs_file_name() ||
file_name == "install_runtime.cmd" ||
is_linux_payload_filename(file_name);
}

View file

@ -19,6 +19,8 @@ std::string bridge_network_library_path(const boost::filesystem::path& plugin_fo
std::string linux_network_library_name();
std::string linux_source_library_name();
std::string host_executable_file_name();
std::string host_executable_abi1_file_name();
std::string host_executable_abi0_file_name();
std::string mac_host_wrapper_file_name();
std::string windows_wsl_distro_file_name();
std::string windows_wsl_import_script_file_name();

View file

@ -108,7 +108,7 @@ std::filesystem::path configured_plugin_cache_dir()
const auto appdata = required_env("APPDATA");
if (!appdata.empty())
return std::filesystem::path(appdata) / "OrcaSlicer" / "plugins";
return std::filesystem::path(appdata) / "OrcaSlicer" / "ota";
return {};
}
@ -145,6 +145,8 @@ std::string first_missing_runtime_file(const std::filesystem::path& plugin_dir)
{
for (const auto& name : {
host_executable_file_name(),
host_executable_abi1_file_name(),
host_executable_abi0_file_name(),
windows_wsl_distro_file_name()
}) {
if (!std::filesystem::exists(plugin_dir / name))

View file

@ -1,37 +1,46 @@
cmake_minimum_required(VERSION 3.16)
cmake_policy(SET CMP0167 NEW)
project(pjarczak_bambu_linux_host_standalone LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_EXTENSIONS OFF)
get_filename_component(REPO_ROOT "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(Boost CONFIG REQUIRED COMPONENTS filesystem)
add_executable(pjarczak_bambu_linux_host
"${REPO_ROOT}/tools/pjarczak_bambu_linux_host/main.cpp"
"${REPO_ROOT}/tools/pjarczak_bambu_linux_host/LinuxPluginHost.cpp"
"${REPO_ROOT}/src/slic3r/Utils/PJarczakLinuxBridge/PJarczakLinuxBridgeConfig.cpp"
"${REPO_ROOT}/src/slic3r/Utils/PJarczakLinuxBridge/PJarczakLinuxSoBridgeRpcProtocol.cpp"
set(HOST_SOURCES
"${CMAKE_CURRENT_LIST_DIR}/main.cpp"
"${CMAKE_CURRENT_LIST_DIR}/LinuxPluginHost.cpp"
"${CMAKE_CURRENT_LIST_DIR}/../../src/slic3r/Utils/PJarczakLinuxBridge/PJarczakLinuxBridgeConfig.cpp"
"${CMAKE_CURRENT_LIST_DIR}/../../src/slic3r/Utils/PJarczakLinuxBridge/PJarczakLinuxSoBridgeRpcProtocol.cpp"
)
target_include_directories(pjarczak_bambu_linux_host PRIVATE
"${REPO_ROOT}"
"${REPO_ROOT}/src"
)
function(add_host_variant target_name abi_value)
add_executable(${target_name} ${HOST_SOURCES})
set_target_properties(${target_name} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tools/pjarczak_bambu_linux_host"
OUTPUT_NAME ${target_name}
BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH "$ORIGIN"
)
target_include_directories(${target_name} PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/../.."
"${CMAKE_CURRENT_LIST_DIR}/../../src"
)
target_compile_definitions(${target_name} PRIVATE
PJARCZAK_LINUX_BRIDGE_STANDALONE_HOST=1
PJARCZAK_LINUX_BRIDGE_LIGHTWEIGHT_TASKS=1
_GLIBCXX_USE_CXX11_ABI=${abi_value}
)
target_link_libraries(${target_name} PRIVATE
Boost::filesystem
${CMAKE_DL_LIBS}
OpenSSL::Crypto
)
endfunction()
target_link_libraries(pjarczak_bambu_linux_host PRIVATE
Boost::filesystem
Threads::Threads
OpenSSL::Crypto
${CMAKE_DL_LIBS}
)
add_host_variant(pjarczak_bambu_linux_host_abi1 1)
add_host_variant(pjarczak_bambu_linux_host_abi0 0)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_link_options(pjarczak_bambu_linux_host PRIVATE -static-libstdc++ -static-libgcc)
endif()
add_custom_target(pjarczak_bambu_linux_host ALL
DEPENDS pjarczak_bambu_linux_host_abi1 pjarczak_bambu_linux_host_abi0)

View file

@ -1,6 +1,7 @@
#include "LinuxPluginHost.hpp"
#include "../../shared/pjarczak_linux_plugin_bridge_core/BridgeCoreJson.hpp"
#include "../../shared/pjarczak_linux_plugin_bridge_core/BridgeAuthPayload.hpp"
#include "../../src/slic3r/Utils/bambu_networking.hpp"
#include "../../src/slic3r/GUI/Printer/BambuTunnel.h"
#include "../../src/slic3r/Utils/PJarczakLinuxBridge/PJarczakLinuxBridgeCompat.hpp"
@ -14,7 +15,9 @@
#include <cstdio>
#include <cstdint>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <filesystem>
#include <unistd.h>
using namespace std::chrono_literals;
@ -69,7 +72,7 @@ using fn_ft_job_set_msg_cb = ft_err (*)(FT_JobHandle *, void (*)(void *user, ft_
using fn_ft_job_try_get_msg = ft_err (*)(FT_JobHandle *, ft_job_msg *out_msg);
using fn_ft_job_get_msg = ft_err (*)(FT_JobHandle *, uint32_t timeout_ms, ft_job_msg *out_msg);
bool path_exists(const boost::filesystem::path& path)
bool path_exists(const std::filesystem::path& path)
{
FILE* f = std::fopen(path.string().c_str(), "rb");
if (!f)
@ -85,37 +88,25 @@ std::string env_or(const char* name, const char* fallback)
return fallback;
}
std::string windows_path_to_wsl(std::string path)
std::mutex g_host_log_mutex;
std::string trim_for_log(const std::string& value, std::size_t limit = 8192)
{
if (path.rfind("\\\\?\\", 0) == 0)
path.erase(0, 4);
if (path.size() < 3)
return path;
const char drive = path[0];
const bool drive_ok =
(drive >= 'A' && drive <= 'Z') ||
(drive >= 'a' && drive <= 'z');
if (!drive_ok || path[1] != ':' || (path[2] != '\\' && path[2] != '/'))
return path;
std::string out = "/mnt/";
out.push_back((drive >= 'A' && drive <= 'Z') ? static_cast<char>(drive - 'A' + 'a') : drive);
out.push_back('/');
for (std::size_t i = 3; i < path.size(); ++i)
out.push_back(path[i] == '\\' ? '/' : path[i]);
return out;
if (value.size() <= limit)
return value;
return value.substr(0, limit) + "...<truncated>";
}
std::vector<std::string> windows_paths_to_wsl(std::vector<std::string> paths)
void host_log_json(const std::string& kind, const nlohmann::json& payload)
{
for (auto& path : paths)
path = windows_path_to_wsl(std::move(path));
return paths;
try {
nlohmann::json line;
line["kind"] = kind;
line["payload"] = payload;
std::lock_guard<std::mutex> lock(g_host_log_mutex);
std::cerr << "[PJBRIDGE] " << trim_for_log(line.dump()) << std::endl;
} catch (...) {
}
}
@ -226,8 +217,8 @@ BBL::PrintParams print_params_from_json(const nlohmann::json& j)
p.task_name = j.value("task_name", std::string());
p.project_name = j.value("project_name", std::string());
p.preset_name = j.value("preset_name", std::string());
p.filename = windows_path_to_wsl(j.value("filename", std::string()));
p.config_filename = windows_path_to_wsl(j.value("config_filename", std::string()));
p.filename = j.value("filename", std::string());
p.config_filename = j.value("config_filename", std::string());
p.plate_index = j.value("plate_index", 0);
p.ftp_folder = j.value("ftp_folder", std::string());
p.ftp_file = j.value("ftp_file", std::string());
@ -243,7 +234,7 @@ BBL::PrintParams print_params_from_json(const nlohmann::json& j)
p.stl_design_id = j.value("stl_design_id", 0);
p.origin_model_id = j.value("origin_model_id", std::string());
p.print_type = j.value("print_type", std::string());
p.dst_file = windows_path_to_wsl(j.value("dst_file", std::string()));
p.dst_file = j.value("dst_file", std::string());
p.dev_name = j.value("dev_name", std::string());
p.dev_ip = j.value("dev_ip", std::string());
p.use_ssl_for_ftp = j.value("use_ssl_for_ftp", false);
@ -464,7 +455,7 @@ bool LinuxPluginHost::consume_thread_reply_binary(std::vector<unsigned char>& ou
void LinuxPluginHost::load_modules()
{
const boost::filesystem::path plugin_folder = boost::filesystem::path(env_or("PJARCZAK_BAMBU_PLUGIN_DIR", "."));
const std::filesystem::path plugin_folder = std::filesystem::path(env_or("PJARCZAK_BAMBU_PLUGIN_DIR", "."));
std::string manifest_reason;
const bool have_manifest = path_exists(linux_payload_manifest_path(plugin_folder));
const bool manifest_ok = !have_manifest || validate_linux_payload_set_against_manifest(plugin_folder, &manifest_reason);
@ -694,7 +685,7 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
return {{"ok", true}, {"value", 0}, {"result", refresh_agora_url_ptr_string()}};
}
if (method == "bridge.handshake") {
const boost::filesystem::path plugin_folder = boost::filesystem::path(env_or("PJARCZAK_BAMBU_PLUGIN_DIR", "."));
const std::filesystem::path plugin_folder = std::filesystem::path(env_or("PJARCZAK_BAMBU_PLUGIN_DIR", "."));
nlohmann::json out = nlohmann::json::object();
out["ok"] = true;
out["protocol_version"] = 1;
@ -717,6 +708,29 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
if (method == "bridge.capabilities") {
return {{"ok", true}, {"agent_count", m_agents.size()}, {"auth_capabilities", auth_capabilities()}};
}
if (method == "bridge.runtime_info") {
char cwd_buf[4096] = {0};
std::string cwd;
if (::getcwd(cwd_buf, sizeof(cwd_buf) - 1))
cwd = cwd_buf;
nlohmann::json out = nlohmann::json::object();
out["ok"] = true;
out["cwd"] = cwd;
out["home"] = env_or("HOME", "");
out["plugin_dir"] = env_or("PJARCZAK_BAMBU_PLUGIN_DIR", "");
out["network_so"] = env_or("PJARCZAK_BAMBU_NETWORK_SO", "");
out["source_so"] = env_or("PJARCZAK_BAMBU_SOURCE_SO", "");
out["ssl_cert_file"] = env_or("SSL_CERT_FILE", "");
out["ssl_cert_dir"] = env_or("SSL_CERT_DIR", "");
out["curl_ca_bundle"] = env_or("CURL_CA_BUNDLE", "");
out["ld_library_path"] = env_or("LD_LIBRARY_PATH", "");
out["network_loaded"] = m_network != nullptr;
out["source_loaded"] = m_source != nullptr;
out["network_status"] = m_network_status;
out["source_status"] = m_source_status;
host_log_json("bridge.runtime_info", out);
return out;
}
if (method == "bridge.job_cancel") {
set_job_cancel(payload.value("job_id", 0LL), payload.value("cancel", true));
@ -734,8 +748,7 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
if (method == "net.create_agent") {
auto f = net<void* (*)(std::string)>("bambu_network_create_agent");
if (!f) return not_supported(method);
const std::string log_dir = windows_path_to_wsl(payload.value("log_dir", std::string()));
void* raw = f(log_dir);
void* raw = f(payload.value("log_dir", std::string()));
const auto id = m_next_agent++;
{
std::lock_guard<std::mutex> lock(m_state_mutex);
@ -796,14 +809,18 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
if (method == "net.set_config_dir") {
auto f = net<int (*)(void*, std::string)>("bambu_network_set_config_dir");
auto a = lookup_agent();
const std::string config_dir = windows_path_to_wsl(payload.value("config_dir", std::string()));
return f && a ? nlohmann::json{{"ok", true}, {"value", f(a, config_dir)}} : not_supported(method);
return f && a ? nlohmann::json{{"ok", true}, {"value", f(a, payload.value("config_dir", std::string()))}} : not_supported(method);
}
if (method == "net.set_cert_file") {
auto f = net<int (*)(void*, std::string, std::string)>("bambu_network_set_cert_file");
auto a = lookup_agent();
const std::string folder = windows_path_to_wsl(payload.value("folder", std::string()));
return f && a ? nlohmann::json{{"ok", true}, {"value", f(a, folder, payload.value("filename", std::string()))}} : not_supported(method);
if (!f || !a) return not_supported(method);
const auto folder = payload.value("folder", std::string());
const auto filename = payload.value("filename", std::string());
const int ret = f(a, folder, filename);
nlohmann::json r{{"ok", true}, {"value", ret}, {"folder", folder}, {"filename", filename}, {"ssl_cert_file", env_or("SSL_CERT_FILE", "")}, {"curl_ca_bundle", env_or("CURL_CA_BUNDLE", "")}};
host_log_json("net.set_cert_file", r);
return r;
}
if (method == "net.set_country_code") {
auto f = net<int (*)(void*, std::string)>("bambu_network_set_country_code");
@ -928,8 +945,10 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
auto f = net<int (*)(void*, std::string)>("bambu_network_change_user");
auto a = lookup_agent();
if (!f || !a) return not_supported(method);
const int ret = f(a, payload.value("user_info", std::string()));
nlohmann::json r{{"ok", true}, {"value", ret}};
const auto original_user_info = payload.value("user_info", std::string());
const auto normalized_user_info = normalize_change_user_payload_string(original_user_info);
const int ret = f(a, normalized_user_info);
nlohmann::json r{{"ok", true}, {"value", ret}, {"user_info_original", original_user_info}, {"user_info_normalized", normalized_user_info}, {"user_info_was_normalized", normalized_user_info != original_user_info}};
auto g1 = net<bool (*)(void*)>("bambu_network_is_user_login"); if (g1) r["logged_in"] = g1(a);
auto g2 = net<std::string (*)(void*)>("bambu_network_get_user_id"); if (g2) r["user_id"] = g2(a);
auto g3 = net<std::string (*)(void*)>("bambu_network_get_user_name"); if (g3) r["user_name"] = g3(a);
@ -939,6 +958,7 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
auto g7 = net<std::string (*)(void*)>("bambu_network_build_logout_cmd"); if (g7) r["logout_cmd"] = g7(a);
auto g8 = net<std::string (*)(void*)>("bambu_network_build_login_info"); if (g8) r["login_info"] = g8(a);
auto g9 = net<std::string (*)(void*)>("bambu_network_get_bambulab_host"); if (g9) r["bambulab_host"] = g9(a);
host_log_json("net.change_user", r);
return r;
}
@ -1104,7 +1124,7 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
if (method == "net.get_setting_list2") { auto f = net<int (*)(void*, std::string, CheckFn, ProgressFn, WasCancelledFn)>("bambu_network_get_setting_list2"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); const auto job_id = payload.value("client_job_id", 0LL); const auto params = payload.value("params", nlohmann::json::object()); auto job = std::make_shared<HostJobState>(); job->job_id = job_id; job->agent_handle = agent_id; job->kind = "get_setting_list2"; register_job(job); const int ret = f(a, params.value("bundle_version", std::string()), [this, job](std::map<std::string, std::string> info) { const auto request_id = m_next_wait_request.fetch_add(1); { std::lock_guard<std::mutex> lock(job->wait_mutex); job->wait_request_id = request_id; job->wait_reply_ready = false; job->wait_reply_value = true; } queue_event(job->agent_handle, "job.check", {{"job_id", job->job_id}, {"kind", job->kind}, {"request_id", request_id}, {"info", info}}); std::unique_lock<std::mutex> lock(job->wait_mutex); job->wait_cv.wait(lock, [&] { return job->wait_reply_ready && job->wait_request_id == request_id; }); return job->wait_reply_value; }, [this, job](int progress) { queue_event(job->agent_handle, "job.progress", {{"job_id", job->job_id}, {"kind", job->kind}, {"progress", progress}}); }, [job]() { return job->cancel_requested.load(); }); unregister_job(job_id); return {{"ok", true}, {"value", ret}, {"job_id", job_id}}; }
if (method == "net.put_setting") { auto f = net<int (*)(void*, std::string, std::string, std::map<std::string, std::string>*, unsigned int*)>("bambu_network_put_setting"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); auto values = json_to_string_map(payload.value("values", nlohmann::json::object())); unsigned int http_code = 0; const int ret = f(a, payload.value("setting_id", std::string()), payload.value("name", std::string()), &values, &http_code); return {{"ok", true}, {"value", ret}, {"http_code", http_code}}; }
if (method == "net.delete_setting") { auto f = net<int (*)(void*, std::string)>("bambu_network_delete_setting"); auto a = lookup_agent(); return f && a ? nlohmann::json{{"ok", true}, {"value", f(a, payload.value("setting_id", std::string()))}} : not_supported(method); }
if (method == "net.set_extra_http_header") { auto f = net<int (*)(void*, std::map<std::string, std::string>)>("bambu_network_set_extra_http_header"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); auto headers = json_to_string_map(payload.value("headers", nlohmann::json::object())); return {{"ok", true}, {"value", f(a, headers)}}; }
if (method == "net.set_extra_http_header") { auto f = net<int (*)(void*, std::map<std::string, std::string>)>("bambu_network_set_extra_http_header"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); auto headers = json_to_string_map(payload.value("headers", nlohmann::json::object())); const int ret = f(a, headers); nlohmann::json r{{"ok", true}, {"value", ret}, {"headers", headers}}; host_log_json("net.set_extra_http_header", r); return r; }
if (method == "net.get_my_message") { auto f = net<int (*)(void*, int, int, int, unsigned int*, std::string*)>("bambu_network_get_my_message"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); unsigned int http_code = 0; std::string http_body; const int ret = f(a, payload.value("type", 0), payload.value("after", 0), payload.value("limit", 20), &http_code, &http_body); return {{"ok", true}, {"value", ret}, {"http_code", http_code}, {"http_body", http_body}}; }
if (method == "net.check_user_task_report") { auto f = net<int (*)(void*, int*, bool*)>("bambu_network_check_user_task_report"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); int task_id = 0; bool printable = false; const int ret = f(a, &task_id, &printable); return {{"ok", true}, {"value", ret}, {"task_id", task_id}, {"printable", printable}}; }
if (method == "net.get_user_print_info") { auto f = net<int (*)(void*, unsigned int*, std::string*)>("bambu_network_get_user_print_info"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); unsigned int http_code = 0; std::string http_body; const int ret = f(a, &http_code, &http_body); return {{"ok", true}, {"value", ret}, {"http_code", http_code}, {"http_body", http_body}}; }
@ -1118,13 +1138,13 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
if (method == "net.get_model_mall_home_url") { auto f = net<int (*)(void*, std::string*)>("bambu_network_get_model_mall_home_url"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); std::string url; const int ret = f(a, &url); return {{"ok", true}, {"value", ret}, {"url", url}}; }
if (method == "net.get_model_mall_detail_url") { auto f = net<int (*)(void*, std::string*, std::string)>("bambu_network_get_model_mall_detail_url"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); std::string url; const int ret = f(a, &url, payload.value("id", std::string())); return {{"ok", true}, {"value", ret}, {"url", url}}; }
if (method == "net.get_subtask") { auto f = net<int (*)(void*, Slic3r::BBLModelTask*, OnGetSubTaskFn)>("bambu_network_get_subtask"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); Slic3r::BBLModelTask task{}; if (payload.contains("task") && payload["task"].is_object()) json_to_model_task(payload["task"], task); return wait_model_task_callback([&](auto cb) { return f(a, &task, cb); }); }
if (method == "net.put_model_mall_rating") { auto f = net<int (*)(void*, int, int, std::string, std::vector<std::string>, unsigned int&, std::string&)>("bambu_network_put_model_mall_rating"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); std::vector<std::string> images = windows_paths_to_wsl(payload.value("images", std::vector<std::string>())); unsigned int http_code = 0; std::string http_error; const int ret = f(a, payload.value("rating_id", 0), payload.value("score", 0), payload.value("content", std::string()), images, http_code, http_error); return {{"ok", true}, {"value", ret}, {"http_code", http_code}, {"http_error", http_error}}; }
if (method == "net.put_model_mall_rating") { auto f = net<int (*)(void*, int, int, std::string, std::vector<std::string>, unsigned int&, std::string&)>("bambu_network_put_model_mall_rating"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); unsigned int http_code = 0; std::string http_error; const int ret = f(a, payload.value("rating_id", 0), payload.value("score", 0), payload.value("content", std::string()), payload.value("images", std::vector<std::string>()), http_code, http_error); return {{"ok", true}, {"value", ret}, {"http_code", http_code}, {"http_error", http_error}}; }
if (method == "net.get_oss_config") { auto f = net<int (*)(void*, std::string&, std::string, unsigned int&, std::string&)>("bambu_network_get_oss_config"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); std::string config; unsigned int http_code = 0; std::string http_error; const int ret = f(a, config, payload.value("country_code", std::string()), http_code, http_error); return {{"ok", true}, {"value", ret}, {"config", config}, {"http_code", http_code}, {"http_error", http_error}}; }
if (method == "net.put_rating_picture_oss") { auto f = net<int (*)(void*, std::string&, std::string&, std::string, int, unsigned int&, std::string&)>("bambu_network_put_rating_picture_oss"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); std::string config = payload.value("config", std::string()); std::string pic_oss_path = windows_path_to_wsl(payload.value("pic_oss_path", std::string())); unsigned int http_code = 0; std::string http_error; const int ret = f(a, config, pic_oss_path, payload.value("model_id", std::string()), payload.value("profile_id", 0), http_code, http_error); return {{"ok", true}, {"value", ret}, {"config", config}, {"pic_oss_path", pic_oss_path}, {"http_code", http_code}, {"http_error", http_error}}; }
if (method == "net.put_rating_picture_oss") { auto f = net<int (*)(void*, std::string&, std::string&, std::string, int, unsigned int&, std::string&)>("bambu_network_put_rating_picture_oss"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); std::string config = payload.value("config", std::string()); std::string pic_oss_path = payload.value("pic_oss_path", std::string()); unsigned int http_code = 0; std::string http_error; const int ret = f(a, config, pic_oss_path, payload.value("model_id", std::string()), payload.value("profile_id", 0), http_code, http_error); return {{"ok", true}, {"value", ret}, {"config", config}, {"pic_oss_path", pic_oss_path}, {"http_code", http_code}, {"http_error", http_error}}; }
if (method == "net.get_model_mall_rating") { auto f = net<int (*)(void*, int, std::string&, unsigned int&, std::string&)>("bambu_network_get_model_mall_rating"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); std::string rating_result; unsigned int http_code = 0; std::string http_error; const int ret = f(a, payload.value("job_id", 0), rating_result, http_code, http_error); return {{"ok", true}, {"value", ret}, {"rating_result", rating_result}, {"http_code", http_code}, {"http_error", http_error}}; }
if (method == "net.get_mw_user_preference") { auto f = net<int (*)(void*, std::function<void(std::string)>)>("bambu_network_get_mw_user_preference"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); return wait_string_callback([&](auto cb) { return f(a, cb); }); }
if (method == "net.get_mw_user_4ulist") { auto f = net<int (*)(void*, int, int, std::function<void(std::string)>)>("bambu_network_get_mw_user_4ulist"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); return wait_string_callback([&](auto cb) { return f(a, payload.value("seed", 0), payload.value("limit", 0), cb); }); }
if (method == "net.get_hms_snapshot") { auto f = net<int (*)(void*, std::string, std::string, std::function<void(std::string, int)>)>("bambu_network_get_hms_snapshot"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); const std::string file_name = windows_path_to_wsl(payload.value("file_name", std::string())); return wait_string_int_callback([&](auto cb) { return f(a, payload.value("dev_id", std::string()), file_name, cb); }); }
if (method == "net.get_hms_snapshot") { auto f = net<int (*)(void*, std::string, std::string, std::function<void(std::string, int)>)>("bambu_network_get_hms_snapshot"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); return wait_string_int_callback([&](auto cb) { return f(a, payload.value("dev_id", std::string()), payload.value("file_name", std::string()), cb); }); }
if (method == "net.get_my_token") { auto f = net<int (*)(void*, std::string, unsigned int*, std::string*)>("bambu_network_get_my_token"); auto a = lookup_agent(); if (!f || !a) return not_supported(method); unsigned int http_code = 0; std::string http_body; const int ret = f(a, payload.value("ticket", std::string()), &http_code, &http_body); return {{"ok", true}, {"value", ret}, {"http_code", http_code}, {"http_body", http_body}}; }
@ -1374,8 +1394,7 @@ nlohmann::json LinuxPluginHost::handle(const std::string& method, const nlohmann
auto f = src<int (*)(Bambu_Tunnel*, const char*)>("Bambu_Create");
if (!f) return not_supported(method);
Bambu_Tunnel tunnel = nullptr;
const std::string path = windows_path_to_wsl(payload.value("path", std::string()));
const int ret = f(&tunnel, path.c_str());
const int ret = f(&tunnel, payload.value("path", std::string()).c_str());
if (ret != 0)
return {{"ok", true}, {"value", ret}, {"tunnel", 0}};
const auto id = m_next_tunnel++;

View file

@ -4,56 +4,43 @@ set -euo pipefail
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
PROJECT_DIR="$(CDPATH= cd -- "$SCRIPT_DIR/../.." && pwd)"
RUNTIME_ROOT="$PROJECT_DIR/tools/pjarczak_bambu_linux_host/runtime/linux-x86_64"
RUNTIME_LIB_DIR="$RUNTIME_ROOT/pjarczak_bambu_linux_host.runtime"
find_host_bin() {
if [[ $# -gt 0 && -n "${1:-}" ]]; then
if [[ -f "$1" ]]; then
printf '%s\n' "$1"
return 0
fi
if [[ -d "$1" ]]; then
find "$1" -type f -name pjarczak_bambu_linux_host | head -n 1
return 0
fi
fi
local name="$1"
local candidate=""
for candidate in \
"$PROJECT_DIR/build/src/Release/pjarczak_bambu_linux_host" \
"$PROJECT_DIR/build/pjarczak_bambu_linux_host" \
"$PROJECT_DIR/build/src/pjarczak_bambu_linux_host"
for candidate in "$PROJECT_DIR/build/src/Release/$name" "$PROJECT_DIR/build/$name" "$PROJECT_DIR/build/src/$name"
do
if [[ -f "$candidate" ]]; then
printf '%s\n' "$candidate"
printf '%s
' "$candidate"
return 0
fi
done
find "$PROJECT_DIR/build" -type f -name "$name" 2>/dev/null | head -n 1
}
find "$PROJECT_DIR/build" -type f -name pjarczak_bambu_linux_host 2>/dev/null | head -n 1
collect_runtime_libs() {
local host_bin="$1"
ldd "$host_bin" | awk '
/=>/ && $3 ~ /^\// { print $3 }
/^\// { print $1 }
' | sort -u
}
copy_runtime_libs() {
local host_bin="$1"
mkdir -p "$RUNTIME_LIB_DIR"
mapfile -t libs < <(
ldd "$host_bin" | awk '
/=>/ && $3 ~ /^\// { print $3 }
/^\// { print $1 }
' | sort -u
)
local lib=""
local base=""
local host_abi1="$1"
local host_abi0="$2"
mkdir -p "$RUNTIME_ROOT"
mapfile -t libs < <({ collect_runtime_libs "$host_abi1"; collect_runtime_libs "$host_abi0"; } | sort -u)
local lib base
for lib in "${libs[@]}"; do
base="$(basename -- "$lib")"
case "$base" in
ld-linux*|libc.so.*|libm.so.*|libpthread.so.*|librt.so.*|libdl.so.*|libresolv.so.*|libnsl.so.*|libutil.so.*|libgcc_s.so.*)
ld-linux*|libc.so.*|libm.so.*|libpthread.so.*|librt.so.*|libdl.so.*|libresolv.so.*|libnsl.so.*|libutil.so.*)
continue
;;
esac
cp -Lf "$lib" "$RUNTIME_LIB_DIR/"
cp -Lf "$lib" "$RUNTIME_ROOT/"
done
}
@ -62,21 +49,25 @@ if [[ "$(uname -m)" != "x86_64" ]]; then
exit 1
fi
HOST_BIN="$(find_host_bin "${1:-}" || true)"
if [[ -z "$HOST_BIN" || ! -f "$HOST_BIN" ]]; then
echo "failed to find built pjarczak_bambu_linux_host under $PROJECT_DIR/build" >&2
echo "build it first in the full Orca Linux build context, for example:" >&2
HOST_ABI1="$(find_host_bin pjarczak_bambu_linux_host_abi1 || true)"
HOST_ABI0="$(find_host_bin pjarczak_bambu_linux_host_abi0 || true)"
if [[ -z "$HOST_ABI1" || ! -f "$HOST_ABI1" || -z "$HOST_ABI0" || ! -f "$HOST_ABI0" ]]; then
echo "failed to find built pjarczak_bambu_linux_host_abi1/abi0 under $PROJECT_DIR/build" >&2
echo "build them first in the full Orca Linux build context, for example:" >&2
echo " cmake --build build --config Release --target pjarczak_bambu_linux_host" >&2
exit 1
fi
rm -rf "$RUNTIME_ROOT"
mkdir -p "$RUNTIME_ROOT" "$RUNTIME_LIB_DIR"
mkdir -p "$RUNTIME_ROOT"
cp -f "$HOST_BIN" "$RUNTIME_ROOT/pjarczak_bambu_linux_host"
chmod +x "$RUNTIME_ROOT/pjarczak_bambu_linux_host"
cp -f "$PROJECT_DIR/tools/pjarczak_bambu_runtime/wsl/pjarczak_bambu_linux_host" "$RUNTIME_ROOT/pjarczak_bambu_linux_host"
cp -f "$HOST_ABI1" "$RUNTIME_ROOT/pjarczak_bambu_linux_host_abi1"
cp -f "$HOST_ABI0" "$RUNTIME_ROOT/pjarczak_bambu_linux_host_abi0"
chmod +x "$RUNTIME_ROOT/pjarczak_bambu_linux_host" "$RUNTIME_ROOT/pjarczak_bambu_linux_host_abi1" "$RUNTIME_ROOT/pjarczak_bambu_linux_host_abi0"
copy_runtime_libs "$HOST_BIN"
copy_runtime_libs "$HOST_ABI1" "$HOST_ABI0"
echo "linux host runtime packaged into:"
echo " $RUNTIME_ROOT"
find "$RUNTIME_ROOT" -maxdepth 1 -type f | sort

View file

@ -1,61 +1,53 @@
param(
[Parameter(Mandatory = $true)][string]$OutputDir,
[Parameter(Mandatory = $true)][string]$RootFs,
[Parameter(Mandatory = $true)][string]$LinuxHostBinary,
[string]$RuntimeDir = "",
[string]$BridgeDll = "",
[string]$DistroName = "PJARCZAK-BAMBU"
)
$ErrorActionPreference = 'Stop'
function Get-ScriptDir {
if ($PSScriptRoot) {
return $PSScriptRoot
}
if ($PSCommandPath) {
return (Split-Path -Parent $PSCommandPath)
}
if ($MyInvocation -and $MyInvocation.MyCommand -and $MyInvocation.MyCommand.Path) {
return (Split-Path -Parent $MyInvocation.MyCommand.Path)
}
return (Get-Location).Path
}
$scriptDir = Get-ScriptDir
$toolsRoot = [System.IO.Path]::GetFullPath((Join-Path $scriptDir '..'))
$wslRoot = Join-Path $toolsRoot 'wsl'
if (-not (Test-Path $RootFs)) { throw "RootFs not found: $RootFs" }
if (-not (Test-Path $LinuxHostBinary)) { throw "LinuxHostBinary not found: $LinuxHostBinary" }
if ($RuntimeDir -and -not (Test-Path $RuntimeDir)) { throw "RuntimeDir not found: $RuntimeDir" }
if ($BridgeDll -and -not (Test-Path $BridgeDll)) { throw "BridgeDll not found: $BridgeDll" }
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
Copy-Item -Force (Join-Path $wslRoot 'install_runtime.ps1') (Join-Path $OutputDir 'install_runtime.ps1')
Copy-Item -Force (Join-Path $wslRoot 'install_runtime.cmd') (Join-Path $OutputDir 'install_runtime.cmd')
Copy-Item -Force (Join-Path $wslRoot 'verify_runtime.ps1') (Join-Path $OutputDir 'verify_runtime.ps1')
Copy-Item -Force (Join-Path $wslRoot 'pjarczak_wsl_run_host.sh') (Join-Path $OutputDir 'pjarczak_wsl_run_host.sh')
Copy-Item -Force (Join-Path $wslRoot 'pjarczak_wsl_distro.txt') (Join-Path $OutputDir 'pjarczak_wsl_distro.txt')
Set-Content -Path (Join-Path $OutputDir 'pjarczak_wsl_distro.txt') -Value ($DistroName + [Environment]::NewLine) -NoNewline:$false
Copy-Item -Force $RootFs (Join-Path $OutputDir 'windows-wsl2-rootfs.tar')
Copy-Item -Force $LinuxHostBinary (Join-Path $OutputDir 'pjarczak_bambu_linux_host')
if ($BridgeDll) {
Copy-Item -Force $BridgeDll (Join-Path $OutputDir 'pjarczak_bambu_networking_bridge.dll')
}
if ($RuntimeDir) {
$destRuntime = Join-Path $OutputDir 'pjarczak_bambu_linux_host.runtime'
if (Test-Path $destRuntime) {
Remove-Item -Recurse -Force $destRuntime
}
New-Item -ItemType Directory -Force -Path $destRuntime | Out-Null
Copy-Item -Recurse -Force (Join-Path $RuntimeDir '*') $destRuntime
}
Write-Host 'Bundle created:'
Write-Host $OutputDir
param(
[Parameter(Mandatory = $true)][string]$OutputDir,
[Parameter(Mandatory = $true)][string]$RootFs,
[Parameter(Mandatory = $true)][string]$LinuxHostAbi1,
[Parameter(Mandatory = $true)][string]$LinuxHostAbi0,
[string]$LinuxHostWrapper = "",
[string]$BridgeDll = "",
[string]$DistroName = "PJARCZAK-BAMBU"
)
$ErrorActionPreference = 'Stop'
function Get-ScriptDir {
if ($PSScriptRoot) { return $PSScriptRoot }
if ($PSCommandPath) { return (Split-Path -Parent $PSCommandPath) }
if ($MyInvocation -and $MyInvocation.MyCommand -and $MyInvocation.MyCommand.Path) { return (Split-Path -Parent $MyInvocation.MyCommand.Path) }
return (Get-Location).Path
}
$scriptDir = Get-ScriptDir
$toolsRoot = [System.IO.Path]::GetFullPath((Join-Path $scriptDir '..'))
$wslRoot = Join-Path $toolsRoot 'wsl'
if (-not (Test-Path $RootFs)) { throw "RootFs not found: $RootFs" }
if (-not (Test-Path $LinuxHostAbi1)) { throw "LinuxHostAbi1 not found: $LinuxHostAbi1" }
if (-not (Test-Path $LinuxHostAbi0)) { throw "LinuxHostAbi0 not found: $LinuxHostAbi0" }
if ([string]::IsNullOrWhiteSpace($LinuxHostWrapper)) {
$LinuxHostWrapper = Join-Path $wslRoot 'pjarczak_bambu_linux_host'
}
if (-not (Test-Path $LinuxHostWrapper)) { throw "LinuxHostWrapper not found: $LinuxHostWrapper" }
if ($BridgeDll -and -not (Test-Path $BridgeDll)) { throw "BridgeDll not found: $BridgeDll" }
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
Copy-Item -Force (Join-Path $wslRoot 'install_runtime.ps1') (Join-Path $OutputDir 'install_runtime.ps1')
Copy-Item -Force (Join-Path $wslRoot 'install_runtime.cmd') (Join-Path $OutputDir 'install_runtime.cmd')
Copy-Item -Force (Join-Path $wslRoot 'verify_runtime.ps1') (Join-Path $OutputDir 'verify_runtime.ps1')
Copy-Item -Force (Join-Path $wslRoot 'pjarczak_wsl_run_host.sh') (Join-Path $OutputDir 'pjarczak_wsl_run_host.sh')
Copy-Item -Force (Join-Path $wslRoot 'pjarczak_wsl_distro.txt') (Join-Path $OutputDir 'pjarczak_wsl_distro.txt')
Set-Content -Path (Join-Path $OutputDir 'pjarczak_wsl_distro.txt') -Value ($DistroName + [Environment]::NewLine) -NoNewline:$false
Copy-Item -Force $RootFs (Join-Path $OutputDir 'windows-wsl2-rootfs.tar')
Copy-Item -Force $LinuxHostWrapper (Join-Path $OutputDir 'pjarczak_bambu_linux_host')
Copy-Item -Force $LinuxHostAbi1 (Join-Path $OutputDir 'pjarczak_bambu_linux_host_abi1')
Copy-Item -Force $LinuxHostAbi0 (Join-Path $OutputDir 'pjarczak_bambu_linux_host_abi0')
if ($BridgeDll) {
Copy-Item -Force $BridgeDll (Join-Path $OutputDir 'pjarczak_bambu_networking_bridge.dll')
}
Write-Host 'Bundle created:'
Write-Host $OutputDir

View file

@ -4,29 +4,32 @@ param(
[string]$DistroName = "",
[string]$InstallDir = "",
[switch]$ReplaceExisting,
[switch]$SkipCopyToPluginDir
[switch]$SkipCopyToPluginDir,
[switch]$CoreInstallOnly
)
$ErrorActionPreference = 'Stop'
function Get-ScriptDir {
if (-not [string]::IsNullOrWhiteSpace($PSScriptRoot)) {
return $PSScriptRoot
}
if (-not [string]::IsNullOrWhiteSpace($PSCommandPath)) {
return (Split-Path -Parent $PSCommandPath)
}
if ($MyInvocation.MyCommand -and -not [string]::IsNullOrWhiteSpace($MyInvocation.MyCommand.Path)) {
return (Split-Path -Parent $MyInvocation.MyCommand.Path)
}
if ($PSScriptRoot) { return $PSScriptRoot }
if ($PSCommandPath) { return (Split-Path -Parent $PSCommandPath) }
if ($MyInvocation.MyCommand -and $MyInvocation.MyCommand.Path) { return (Split-Path -Parent $MyInvocation.MyCommand.Path) }
return (Get-Location).Path
}
function Convert-FileToLf([string]$Path) {
if ([string]::IsNullOrWhiteSpace($Path) -or !(Test-Path $Path)) {
return
}
function Test-IsAdmin {
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($id)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Invoke-Native([string]$FilePath, [string[]]$ArgumentList) {
& $FilePath @ArgumentList
return $LASTEXITCODE
}
function Convert-FileToLf([string]$Path) {
if ([string]::IsNullOrWhiteSpace($Path) -or !(Test-Path $Path)) { return }
$content = [System.IO.File]::ReadAllText($Path)
$content = $content.Replace("`r`n", "`n").Replace("`r", "`n")
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
@ -34,82 +37,78 @@ function Convert-FileToLf([string]$Path) {
}
function Copy-IfExists([string]$Source, [string]$Destination) {
if (Test-Path $Source) {
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Destination) | Out-Null
Copy-Item -Force $Source $Destination
}
if (!(Test-Path $Source)) { return }
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Destination) | Out-Null
Copy-Item -Force $Source $Destination
}
function Sync-Directory([string]$SourceDir, [string]$DestinationDir) {
if (!(Test-Path $SourceDir)) {
return
}
if (Test-Path $DestinationDir) {
Remove-Item -Recurse -Force $DestinationDir
}
New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null
Copy-Item -Recurse -Force (Join-Path $SourceDir '*') $DestinationDir
}
function Resolve-DistroName([string]$Dir, [string]$Current) {
if (-not [string]::IsNullOrWhiteSpace($Current)) {
return $Current
}
$distroFile = Join-Path $Dir 'pjarczak_wsl_distro.txt'
function Resolve-DistroName([string]$BaseDir, [string]$Current) {
if (-not [string]::IsNullOrWhiteSpace($Current)) { return $Current }
if ($env:PJARCZAK_WSL_DISTRO) { return $env:PJARCZAK_WSL_DISTRO.Trim() }
$distroFile = Join-Path $BaseDir 'pjarczak_wsl_distro.txt'
if (Test-Path $distroFile) {
$value = (Get-Content $distroFile -Raw).Trim()
if (-not [string]::IsNullOrWhiteSpace($value)) {
return $value
}
if (-not [string]::IsNullOrWhiteSpace($value)) { return $value }
}
if ($env:PJARCZAK_WSL_DISTRO) {
return $env:PJARCZAK_WSL_DISTRO.Trim()
}
return 'PJARCZAK-BAMBU'
return 'BambuBridge-OrcaSlicer'
}
$scriptDir = Get-ScriptDir
$defaultPackageDir = $scriptDir
if ([string]::IsNullOrWhiteSpace($PackageDir)) {
$PackageDir = $defaultPackageDir
$PackageDir = Get-ScriptDir
}
$PackageDir = [System.IO.Path]::GetFullPath($PackageDir)
$DistroName = Resolve-DistroName $PackageDir $DistroName
if ([string]::IsNullOrWhiteSpace($PluginDir)) {
if (-not $env:APPDATA) {
throw 'APPDATA is not available'
}
if (-not $env:APPDATA) { throw 'APPDATA is not available' }
$PluginDir = Join-Path $env:APPDATA 'OrcaSlicer\plugins'
}
$PluginDir = [System.IO.Path]::GetFullPath($PluginDir)
$PluginCacheDir = if ($env:APPDATA) { Join-Path $env:APPDATA 'OrcaSlicer\ota' } else { '' }
$DistroName = Resolve-DistroName $PackageDir $DistroName
if ([string]::IsNullOrWhiteSpace($InstallDir)) {
if (-not $env:LOCALAPPDATA) {
throw 'LOCALAPPDATA is not available'
}
if (-not $env:LOCALAPPDATA) { throw 'LOCALAPPDATA is not available' }
$InstallDir = Join-Path $env:LOCALAPPDATA $DistroName
}
$InstallDir = [System.IO.Path]::GetFullPath($InstallDir)
$wsl = Join-Path $env:WINDIR 'System32\wsl.exe'
if (!(Test-Path $wsl)) {
throw 'wsl.exe not found'
throw 'wsl.exe not found. This Windows build does not expose WSL commands.'
}
$bootstrapPath = Join-Path $PackageDir 'pjarczak_wsl_run_host.sh'
if (Test-Path $bootstrapPath) {
Convert-FileToLf $bootstrapPath
}
$packageFiles = @(
'pjarczak_bambu_linux_host',
'pjarczak_bambu_linux_host_abi1',
'pjarczak_bambu_linux_host_abi0',
'pjarczak_wsl_run_host.sh',
'pjarczak_wsl_distro.txt',
'install_runtime.ps1',
'install_runtime.cmd',
'verify_runtime.ps1',
'windows-wsl2-rootfs.tar'
)
foreach ($name in $packageFiles) {
if (!(Test-Path (Join-Path $PackageDir $name))) {
throw "Missing package file: $name"
}
}
if (-not $SkipCopyToPluginDir) {
New-Item -ItemType Directory -Force -Path $PluginDir | Out-Null
$fileNames = @(
$copyNames = @(
'pjarczak_bambu_networking_bridge.dll',
'pjarczak_bambu_linux_host',
'pjarczak_wsl_distro.txt',
'pjarczak_bambu_linux_host_abi1',
'pjarczak_bambu_linux_host_abi0',
'pjarczak_wsl_run_host.sh',
'pjarczak-wsl-run-host.sh',
'pjarczak_wsl_distro.txt',
'install_runtime.ps1',
'install_runtime.cmd',
'verify_runtime.ps1',
@ -121,74 +120,97 @@ if (-not $SkipCopyToPluginDir) {
'libBambuSource.so',
'liblive555.so',
'libagora_rtc_sdk.so',
'libagora-fdkaac.so'
'libagora-fdkaac.so',
'libz.so.1',
'libzstd.so.1',
'libcrypto.so.3',
'libstdc++.so.6',
'libgcc_s.so.1',
'ca-certificates.crt',
'slicer_base64.cer'
)
foreach ($name in $fileNames) {
foreach ($name in $copyNames) {
Copy-IfExists (Join-Path $PackageDir $name) (Join-Path $PluginDir $name)
}
Sync-Directory (Join-Path $PackageDir 'pjarczak_bambu_linux_host.runtime') (Join-Path $PluginDir 'pjarczak_bambu_linux_host.runtime')
$PackageDir = $PluginDir
}
$requiredFiles = @(
'pjarczak_bambu_networking_bridge.dll',
'pjarczak_bambu_linux_host',
'pjarczak_wsl_distro.txt',
'install_runtime.ps1',
'verify_runtime.ps1',
'windows-wsl2-rootfs.tar'
)
foreach ($name in $requiredFiles) {
$path = Join-Path $PackageDir $name
if (!(Test-Path $path)) {
throw "Missing package file: $name"
}
}
$bootstrapPath = Join-Path $PackageDir 'pjarczak_wsl_run_host.sh'
if (!(Test-Path $bootstrapPath)) { $bootstrapPath = Join-Path $PackageDir 'pjarczak-wsl-run-host.sh' }
if (!(Test-Path $bootstrapPath)) {
throw 'Missing package file: pjarczak_wsl_run_host.sh'
}
$needCoreInstall = $false
try {
& $wsl --status | Out-Null
if ($LASTEXITCODE -ne 0) { $needCoreInstall = $true }
} catch {
throw 'WSL is not ready. Run as Administrator once and execute: wsl --install --no-distribution ; wsl --update ; then reboot.'
$needCoreInstall = $true
}
Convert-FileToLf $bootstrapPath
if ($needCoreInstall -and -not (Test-IsAdmin)) {
$self = Join-Path $PackageDir 'install_runtime.ps1'
$args = @(
'-NoProfile',
'-ExecutionPolicy', 'Bypass',
'-File', ('"' + $self + '"'),
'-PackageDir', ('"' + $PackageDir + '"'),
'-PluginDir', ('"' + $PluginDir + '"'),
'-DistroName', ('"' + $DistroName + '"'),
'-InstallDir', ('"' + $InstallDir + '"'),
'-SkipCopyToPluginDir',
'-CoreInstallOnly'
)
$proc = Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList $args -Wait -PassThru
if ($proc.ExitCode -ne 0) { exit $proc.ExitCode }
$needCoreInstall = $false
}
$rebootRequired = $false
if ($needCoreInstall) {
Write-Host 'Installing or enabling WSL core components'
$code = Invoke-Native $wsl @('--install', '--no-distribution')
if ($code -eq 1641 -or $code -eq 3010) {
$rebootRequired = $true
} elseif ($code -ne 0) {
throw "wsl --install --no-distribution failed with exit code $code"
}
$code = Invoke-Native $wsl @('--set-default-version', '2')
if ($code -ne 0) { throw "wsl --set-default-version 2 failed with exit code $code" }
$code = Invoke-Native $wsl @('--update')
if ($code -ne 0) { throw "wsl --update failed with exit code $code" }
}
if ($rebootRequired) {
Write-Host 'WSL install requested a reboot.'
exit 3010
}
if ($CoreInstallOnly) {
exit 0
}
& $wsl --status | Out-Null
if ($LASTEXITCODE -ne 0) {
throw 'WSL is still not ready after install/update'
}
$distroList = & $wsl -l -q 2>$null
if ($LASTEXITCODE -ne 0) {
throw 'Failed to query installed WSL distros'
}
$hasDistro = ($distroList | ForEach-Object { $_.Trim() } | Where-Object { $_ -eq $DistroName })
$alreadyInstalled = $distroList | ForEach-Object { $_.Trim() } | Where-Object { $_ -eq $DistroName }
if ($alreadyInstalled) {
if ($ReplaceExisting) {
& $wsl --terminate $DistroName 2>$null | Out-Null
& $wsl --unregister $DistroName
if ($LASTEXITCODE -ne 0) {
throw "Failed to unregister existing distro '$DistroName'"
}
$alreadyInstalled = $null
}
if ($hasDistro -and $ReplaceExisting) {
& $wsl --terminate $DistroName 2>$null | Out-Null
& $wsl --unregister $DistroName
if ($LASTEXITCODE -ne 0) { throw "Failed to unregister existing distro '$DistroName'" }
$hasDistro = $false
}
if (-not $alreadyInstalled) {
if (-not $hasDistro) {
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
$rootFsTar = Join-Path $PackageDir 'windows-wsl2-rootfs.tar'
& $wsl --import $DistroName $InstallDir $rootFsTar --version 2
if ($LASTEXITCODE -ne 0) {
throw "wsl --import failed for distro '$DistroName'"
}
if ($LASTEXITCODE -ne 0) { throw "wsl --import failed for distro '$DistroName'" }
$wslConf = @'
$setupCmd = @"
cat > /etc/wsl.conf <<'WSL_EOF'
[automount]
enabled=true
root=/mnt/
@ -197,24 +219,13 @@ mountFsTab=false
[interop]
enabled=true
appendWindowsPath=false
'@
$setupCmd = @"
cat > /etc/wsl.conf <<'WSL_EOF'
$wslConf
WSL_EOF
mkdir -p /root/.pjarczak-bambu-runtime
"@
& $wsl -d $DistroName --user root -- sh -lc $setupCmd
if ($LASTEXITCODE -ne 0) {
throw "Failed to initialize distro '$DistroName'"
}
if ($LASTEXITCODE -ne 0) { throw "Failed to initialize distro '$DistroName'" }
& $wsl --terminate $DistroName
if ($LASTEXITCODE -ne 0) {
throw "Failed to terminate distro '$DistroName' after initialization"
}
if ($LASTEXITCODE -ne 0) { throw "Failed to terminate distro '$DistroName' after initialization" }
}
$verifyArgs = @(
@ -223,25 +234,11 @@ $verifyArgs = @(
'-File', (Join-Path $PackageDir 'verify_runtime.ps1'),
'-PackageDir', $PackageDir,
'-DistroName', $DistroName,
'-PluginCacheDir', $PluginDir,
'-AllowMissingLinuxPlugin'
'-PluginCacheDir', $PluginCacheDir,
'-AllowMissingLinuxPlugin',
'-SkipProbe'
)
$verifyShell = $null
$pwshCmd = Get-Command pwsh -ErrorAction SilentlyContinue
if ($pwshCmd) {
$verifyShell = $pwshCmd.Source
} else {
$powershellCmd = Get-Command powershell -ErrorAction SilentlyContinue
if ($powershellCmd) {
$verifyShell = $powershellCmd.Source
}
}
if ([string]::IsNullOrWhiteSpace($verifyShell)) {
throw 'No PowerShell host found to run verify_runtime.ps1'
}
& $verifyShell @verifyArgs
& powershell.exe @verifyArgs
if ($LASTEXITCODE -ne 0) {
throw 'verify_runtime.ps1 failed'
}
@ -250,5 +247,4 @@ Write-Host ''
Write-Host "WSL runtime installed to: $PackageDir"
Write-Host "WSL distro: $DistroName"
Write-Host "WSL install dir: $InstallDir"
Write-Host 'Now start OrcaSlicer.'
Write-Host 'On first run let it download bambunetwork, close the app completely, then start it again.'
Write-Host 'On first run let Orca download bambunetwork if needed, then restart Orca.'

View file

@ -0,0 +1,59 @@
#!/bin/sh
set -eu
DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
CACHE_FILE="$DIR/.selected_host_abi"
run_probe() {
bin="$1"
[ -x "$bin" ] || return 1
LD_LIBRARY_PATH="$DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
PJARCZAK_BAMBU_PROBE_LOG_DIR="${PJARCZAK_BAMBU_PROBE_LOG_DIR:-$DIR}" \
PJARCZAK_BAMBU_COUNTRY_CODE="${PJARCZAK_BAMBU_COUNTRY_CODE:-PL}" \
"$bin" --probe-auth >/dev/null 2>&1
}
choose_bin() {
forced="${PJARCZAK_FORCE_HOST_ABI:-}"
if [ -n "$forced" ] && [ -x "$DIR/pjarczak_bambu_linux_host_$forced" ]; then
printf '%s\n' "$DIR/pjarczak_bambu_linux_host_$forced"
return 0
fi
if [ -f "$CACHE_FILE" ]; then
cached=$(cat "$CACHE_FILE" 2>/dev/null || true)
if [ -n "$cached" ] && [ -x "$DIR/pjarczak_bambu_linux_host_$cached" ]; then
printf '%s\n' "$DIR/pjarczak_bambu_linux_host_$cached"
return 0
fi
fi
for abi in abi1 abi0; do
if run_probe "$DIR/pjarczak_bambu_linux_host_$abi"; then
printf '%s' "$abi" > "$CACHE_FILE"
printf '%s\n' "$DIR/pjarczak_bambu_linux_host_$abi"
return 0
fi
done
if [ -x "$DIR/pjarczak_bambu_linux_host_abi1" ]; then
printf '%s\n' "$DIR/pjarczak_bambu_linux_host_abi1"
return 0
fi
if [ -x "$DIR/pjarczak_bambu_linux_host_abi0" ]; then
printf '%s\n' "$DIR/pjarczak_bambu_linux_host_abi0"
return 0
fi
return 1
}
BIN=$(choose_bin) || {
echo "no compatible host ABI variant found" >&2
exit 127
}
if [ "${1:-}" = "--print-bin" ]; then
printf '%s\n' "$BIN"
exit 0
fi
export LD_LIBRARY_PATH="$DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
exec "$BIN" "$@"

View file

@ -1,6 +1,10 @@
#!/bin/sh
set -eu
log() {
printf '%s\n' "[pjarczak_wsl_run_host] $*" >&2
}
MODE="run"
if [ "${1:-}" = "--probe" ]; then
MODE="probe"
@ -10,30 +14,22 @@ fi
PACKAGE_DIR="${1:-${PJARCZAK_BAMBU_WINDOWS_PLUGIN_DIR:-}}"
PLUGIN_CACHE_DIR="${2:-${PJARCZAK_BAMBU_WINDOWS_PLUGIN_CACHE_DIR:-}}"
if [ -z "$PACKAGE_DIR" ]; then
echo "missing Windows package directory path" >&2
log "missing Windows package directory path"
exit 127
fi
RUNTIME_SUBDIR="$PACKAGE_DIR/pjarczak_bambu_linux_host.runtime"
HOST_SRC="$PACKAGE_DIR/pjarczak_bambu_linux_host"
if [ ! -f "$HOST_SRC" ] && [ -f "$RUNTIME_SUBDIR/pjarczak_bambu_linux_host" ]; then
HOST_SRC="$RUNTIME_SUBDIR/pjarczak_bambu_linux_host"
fi
if [ ! -f "$HOST_SRC" ]; then
echo "missing runtime file: $HOST_SRC" >&2
log "missing runtime file: $HOST_SRC"
exit 127
fi
find_payload_file() {
local name="$1"
find_preferred_file() {
name="$1"
if [ -f "$PACKAGE_DIR/$name" ]; then
printf '%s\n' "$PACKAGE_DIR/$name"
return 0
fi
if [ -f "$RUNTIME_SUBDIR/$name" ]; then
printf '%s\n' "$RUNTIME_SUBDIR/$name"
return 0
fi
if [ -n "$PLUGIN_CACHE_DIR" ] && [ -f "$PLUGIN_CACHE_DIR/$name" ]; then
printf '%s\n' "$PLUGIN_CACHE_DIR/$name"
return 0
@ -42,138 +38,191 @@ find_payload_file() {
}
resolve_payload_sources() {
NETWORK_SRC="$(find_payload_file libbambu_networking.so || true)"
SOURCE_SRC="$(find_payload_file libBambuSource.so || true)"
LIVE555_SRC="$(find_payload_file liblive555.so || true)"
MANIFEST_SRC=""
if [ -f "$PACKAGE_DIR/linux_payload_manifest.json" ]; then
MANIFEST_SRC="$PACKAGE_DIR/linux_payload_manifest.json"
elif [ -f "$RUNTIME_SUBDIR/linux_payload_manifest.json" ]; then
MANIFEST_SRC="$RUNTIME_SUBDIR/linux_payload_manifest.json"
elif [ -n "$PLUGIN_CACHE_DIR" ] && [ -f "$PLUGIN_CACHE_DIR/linux_payload_manifest.json" ]; then
MANIFEST_SRC="$PLUGIN_CACHE_DIR/linux_payload_manifest.json"
fi
NETWORK_SRC="$(find_preferred_file libbambu_networking.so || true)"
SOURCE_SRC="$(find_preferred_file libBambuSource.so || true)"
ABI1_SRC="$(find_preferred_file pjarczak_bambu_linux_host_abi1 || true)"
ABI0_SRC="$(find_preferred_file pjarczak_bambu_linux_host_abi0 || true)"
CA_BUNDLE_SRC="$(find_preferred_file ca-certificates.crt || true)"
SLICER_CERT_SRC="$(find_preferred_file slicer_base64.cer || true)"
MANIFEST_SRC="$(find_preferred_file linux_payload_manifest.json || true)"
}
wait_for_payload_sources() {
if [ "$MODE" = "probe" ]; then
return 1
fi
WAIT_SECS="${PJARCZAK_BAMBU_PLUGIN_WAIT_SECS:-300}"
SLEEP_SECS="${PJARCZAK_BAMBU_PLUGIN_WAIT_INTERVAL_SECS:-2}"
DEADLINE=$(( $(date +%s) + WAIT_SECS ))
while :; do
resolve_payload_sources
if [ -n "$NETWORK_SRC" ] && [ -n "$SOURCE_SRC" ]; then
return 0
fi
NOW=$(date +%s)
if [ "$NOW" -ge "$DEADLINE" ]; then
return 1
fi
[ "$NOW" -lt "$DEADLINE" ] || return 1
sleep "$SLEEP_SECS"
done
}
resolve_payload_sources
if [ -z "$NETWORK_SRC" ] || [ -z "$SOURCE_SRC" ]; then
echo "plugin_not_downloaded package_dir=$PACKAGE_DIR plugin_cache_dir=${PLUGIN_CACHE_DIR:-none}" >&2
if [ "$MODE" = "probe" ]; then
exit 3
fi
if ! wait_for_payload_sources; then
echo "plugin_not_downloaded_timeout package_dir=$PACKAGE_DIR plugin_cache_dir=${PLUGIN_CACHE_DIR:-none}" >&2
exit 127
fi
fi
if ! command -v sha256sum >/dev/null 2>&1; then
echo "sha256sum not found inside WSL distro" >&2
exit 127
fi
RUNTIME_BASE="${PJARCZAK_BAMBU_WSL_RUNTIME_DIR:-/root/.pjarczak-bambu-runtime}"
mkdir -p "$RUNTIME_BASE"
RUNTIME_HASH="$({ sha256sum "$HOST_SRC" "$NETWORK_SRC" "$SOURCE_SRC"; [ -n "$LIVE555_SRC" ] && sha256sum "$LIVE555_SRC"; [ -n "$MANIFEST_SRC" ] && sha256sum "$MANIFEST_SRC"; } | sha256sum | cut -d ' ' -f1)"
TARGET_DIR="$RUNTIME_BASE/$RUNTIME_HASH"
CURRENT_DIR="$RUNTIME_BASE/current"
if [ ! -d "$TARGET_DIR" ]; then
TMP_DIR="$RUNTIME_BASE/.tmp-$RUNTIME_HASH-$$"
rm -rf "$TMP_DIR"
mkdir -p "$TMP_DIR"
cp "$HOST_SRC" "$TMP_DIR/pjarczak_bambu_linux_host"
cp "$NETWORK_SRC" "$TMP_DIR/libbambu_networking.so"
cp "$SOURCE_SRC" "$TMP_DIR/libBambuSource.so"
if [ -n "$LIVE555_SRC" ]; then
cp "$LIVE555_SRC" "$TMP_DIR/liblive555.so"
fi
if [ -n "$MANIFEST_SRC" ]; then
cp "$MANIFEST_SRC" "$TMP_DIR/linux_payload_manifest.json"
fi
append_source() {
dst_name="$1"
src_path="$2"
[ -n "$src_path" ] || return 0
[ -f "$src_path" ] || return 0
printf '%s\t%s\n' "$dst_name" "$src_path" >> "$SOURCE_LIST"
}
append_package_extras() {
for path in "$PACKAGE_DIR"/*; do
[ -f "$path" ] || continue
base="$(basename "$path")"
case "$base" in
pjarczak_bambu_linux_host|libbambu_networking.so|libBambuSource.so|liblive555.so|linux_payload_manifest.json)
pjarczak_bambu_linux_host|pjarczak_bambu_linux_host_abi1|pjarczak_bambu_linux_host_abi0|libbambu_networking.so|libBambuSource.so|linux_payload_manifest.json|ca-certificates.crt|slicer_base64.cer)
continue
;;
*.dll|*.ps1|*.txt|*.tar|*.zip|*.cmd|*.bat|*.sh)
continue
;;
esac
cp "$path" "$TMP_DIR/$base"
append_source "$base" "$path"
done
}
if [ -d "$RUNTIME_SUBDIR" ]; then
for path in "$RUNTIME_SUBDIR"/*; do
[ -f "$path" ] || continue
base="$(basename "$path")"
case "$base" in
pjarczak_bambu_linux_host|libbambu_networking.so|libBambuSource.so|liblive555.so|linux_payload_manifest.json)
continue
;;
*.dll|*.ps1|*.txt|*.tar|*.zip|*.cmd|*.bat|*.sh)
continue
;;
esac
cp "$path" "$TMP_DIR/$base"
done
copy_source_list() {
while IFS="$(printf '\t')" read -r dst_name src_path; do
[ -n "$dst_name" ] || continue
cp "$src_path" "$TMP_DIR/$dst_name"
done < "$SOURCE_LIST"
}
compute_runtime_hash() {
{
while IFS="$(printf '\t')" read -r dst_name src_path; do
[ -n "$dst_name" ] || continue
printf '%s\n' "$dst_name"
sha256sum "$src_path"
done < "$SOURCE_LIST"
} | sha256sum | cut -d ' ' -f1
}
list_runtime_files() {
find "$CURRENT_DIR" -maxdepth 1 -type f -printf '%f\n' | sort | tr '\n' ',' | sed 's/,$//'
}
resolve_payload_sources
if [ -z "$NETWORK_SRC" ] || [ -z "$SOURCE_SRC" ]; then
log "plugin_not_downloaded package_dir=$PACKAGE_DIR plugin_cache_dir=${PLUGIN_CACHE_DIR:-none}"
if [ "$MODE" = "probe" ]; then
exit 3
fi
if ! wait_for_payload_sources; then
log "plugin_not_downloaded_timeout package_dir=$PACKAGE_DIR plugin_cache_dir=${PLUGIN_CACHE_DIR:-none}"
exit 127
fi
fi
chmod 755 "$TMP_DIR/pjarczak_bambu_linux_host"
if [ -z "$ABI1_SRC" ] && [ -z "$ABI0_SRC" ]; then
log "missing host ABI binaries in package_dir=$PACKAGE_DIR plugin_cache_dir=${PLUGIN_CACHE_DIR:-none}"
exit 127
fi
if ! command -v sha256sum >/dev/null 2>&1; then
log "sha256sum not found inside WSL distro"
exit 127
fi
export HOME="${HOME:-/root}"
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
export XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
export XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
mkdir -p "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" "$XDG_STATE_HOME" "$XDG_DATA_HOME" "$HOME/.pjarczak-bambu-runtime"
unset APPDATA LOCALAPPDATA USERPROFILE HOMEDRIVE HOMEPATH TEMP TMP TMPDIR
RUNTIME_BASE="${PJARCZAK_BAMBU_WSL_RUNTIME_DIR:-$HOME/.pjarczak-bambu-runtime}"
mkdir -p "$RUNTIME_BASE"
SOURCE_LIST="$(mktemp "$RUNTIME_BASE/.sources.XXXXXX")"
trap 'rm -f "$SOURCE_LIST"' EXIT INT TERM
append_source "pjarczak_bambu_linux_host" "$HOST_SRC"
append_source "libbambu_networking.so" "$NETWORK_SRC"
append_source "libBambuSource.so" "$SOURCE_SRC"
append_source "pjarczak_bambu_linux_host_abi1" "$ABI1_SRC"
append_source "pjarczak_bambu_linux_host_abi0" "$ABI0_SRC"
append_source "linux_payload_manifest.json" "$MANIFEST_SRC"
append_source "ca-certificates.crt" "$CA_BUNDLE_SRC"
append_source "slicer_base64.cer" "$SLICER_CERT_SRC"
append_package_extras
RUNTIME_HASH="$(compute_runtime_hash)"
TARGET_DIR="$RUNTIME_BASE/$RUNTIME_HASH"
CURRENT_DIR="$RUNTIME_BASE/current"
log "mode=$MODE package_dir=$PACKAGE_DIR plugin_cache_dir=${PLUGIN_CACHE_DIR:-none} runtime_hash=$RUNTIME_HASH"
log "wrapper_src=$HOST_SRC"
log "network_src=${NETWORK_SRC:-missing}"
log "source_src=${SOURCE_SRC:-missing}"
log "abi1_src=${ABI1_SRC:-missing}"
log "abi0_src=${ABI0_SRC:-missing}"
log "manifest_src=${MANIFEST_SRC:-missing}"
log "ca_bundle_src=${CA_BUNDLE_SRC:-missing}"
log "slicer_cert_src=${SLICER_CERT_SRC:-missing}"
if [ ! -d "$TARGET_DIR" ]; then
TMP_DIR="$RUNTIME_BASE/.tmp-$RUNTIME_HASH-$$"
rm -rf "$TMP_DIR"
mkdir -p "$TMP_DIR"
copy_source_list
chmod 755 "$TMP_DIR/pjarczak_bambu_linux_host" "$TMP_DIR"/pjarczak_bambu_linux_host_abi* 2>/dev/null || true
mv "$TMP_DIR" "$TARGET_DIR"
log "created_runtime_dir=$TARGET_DIR"
else
log "reusing_runtime_dir=$TARGET_DIR"
fi
rm -rf "$CURRENT_DIR"
ln -s "$TARGET_DIR" "$CURRENT_DIR"
export PJARCZAK_BAMBU_PLUGIN_DIR="$CURRENT_DIR"
export PJARCZAK_BAMBU_NETWORK_SO="$CURRENT_DIR/libbambu_networking.so"
export PJARCZAK_BAMBU_SOURCE_SO="$CURRENT_DIR/libBambuSource.so"
if [ -f "$CURRENT_DIR/liblive555.so" ]; then
export PJARCZAK_BAMBU_LIVE555_SO="$CURRENT_DIR/liblive555.so"
fi
export LD_LIBRARY_PATH="$CURRENT_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
if [ -f "$CURRENT_DIR/ca-certificates.crt" ]; then
export SSL_CERT_FILE="$CURRENT_DIR/ca-certificates.crt"
export CURL_CA_BUNDLE="$CURRENT_DIR/ca-certificates.crt"
fi
if [ -d /etc/ssl/certs ]; then
export SSL_CERT_DIR="/etc/ssl/certs"
fi
BIN_PATH=$("$CURRENT_DIR/pjarczak_bambu_linux_host" --print-bin 2>/tmp/pjarczak-bin.txt || true)
if [ -z "$BIN_PATH" ] || [ ! -x "$BIN_PATH" ]; then
log "failed to resolve host binary"
cat /tmp/pjarczak-bin.txt >&2 || true
exit 127
fi
log "selected_bin=$BIN_PATH"
log "runtime_files=$(list_runtime_files)"
if command -v ldd >/dev/null 2>&1; then
LDD_OUT="$(ldd "$BIN_PATH" 2>&1 || true)"
if printf '%s\n' "$LDD_OUT" | grep -q 'not found'; then
log "ldd reported missing libraries for $BIN_PATH"
printf '%s\n' "$LDD_OUT" >&2
exit 127
fi
fi
if [ "$MODE" = "probe" ]; then
if ! ldd "$CURRENT_DIR/pjarczak_bambu_linux_host" >/tmp/pjarczak-ldd.txt 2>&1; then
cat /tmp/pjarczak-ldd.txt >&2 || true
PROBE_LOG_DIR="/tmp/pjarczak-bambu-probe"
mkdir -p "$PROBE_LOG_DIR"
if ! PJARCZAK_BAMBU_PROBE_LOG_DIR="$PROBE_LOG_DIR" PJARCZAK_BAMBU_COUNTRY_CODE="${PJARCZAK_BAMBU_COUNTRY_CODE:-PL}" "$CURRENT_DIR/pjarczak_bambu_linux_host" --probe-auth >/tmp/pjarczak-probe.txt 2>&1; then
log "probe_auth_failed"
cat /tmp/pjarczak-probe.txt >&2 || true
exit 127
fi
if grep -q 'not found' /tmp/pjarczak-ldd.txt; then
cat /tmp/pjarczak-ldd.txt >&2
exit 127
fi
echo "probe_ok runtime_dir=$CURRENT_DIR runtime_hash=$RUNTIME_HASH"
echo "probe_ok runtime_dir=$CURRENT_DIR runtime_hash=$RUNTIME_HASH bin=$BIN_PATH"
exit 0
fi

View file

@ -2,33 +2,21 @@ param(
[string]$PackageDir = "",
[string]$DistroName = "",
[string]$PluginCacheDir = "",
[switch]$AllowMissingLinuxPlugin
[switch]$AllowMissingLinuxPlugin,
[switch]$SkipProbe
)
$ErrorActionPreference = 'Stop'
if (Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue) {
$script:__pj_prev_native_pref = $PSNativeCommandUseErrorActionPreference
$PSNativeCommandUseErrorActionPreference = $false
}
function Get-ScriptDir {
if (-not [string]::IsNullOrWhiteSpace($PSScriptRoot)) {
return $PSScriptRoot
}
if (-not [string]::IsNullOrWhiteSpace($PSCommandPath)) {
return (Split-Path -Parent $PSCommandPath)
}
if ($MyInvocation.MyCommand -and -not [string]::IsNullOrWhiteSpace($MyInvocation.MyCommand.Path)) {
return (Split-Path -Parent $MyInvocation.MyCommand.Path)
}
if ($PSScriptRoot) { return $PSScriptRoot }
if ($PSCommandPath) { return (Split-Path -Parent $PSCommandPath) }
if ($MyInvocation.MyCommand -and $MyInvocation.MyCommand.Path) { return (Split-Path -Parent $MyInvocation.MyCommand.Path) }
return (Get-Location).Path
}
function Convert-FileToLf([string]$Path) {
if ([string]::IsNullOrWhiteSpace($Path) -or !(Test-Path $Path)) {
return
}
if ([string]::IsNullOrWhiteSpace($Path) -or !(Test-Path $Path)) { return }
$content = [System.IO.File]::ReadAllText($Path)
$content = $content.Replace("`r`n", "`n").Replace("`r", "`n")
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
@ -39,71 +27,11 @@ function To-WslPath([string]$Path) {
$full = [System.IO.Path]::GetFullPath($Path)
if ($full.Length -ge 2 -and $full[1] -eq ':') {
$drive = $full.Substring(0, 1).ToLowerInvariant()
$tail = ($full.Substring(2) -replace '\\', '/')
if ($tail.StartsWith('/')) {
$tail = $tail.Substring(1)
}
$tail = $full.Substring(2).Replace('\', '/')
if ($tail.StartsWith('/')) { $tail = $tail.Substring(1) }
return "/mnt/$drive/$tail"
}
return ($full -replace '\\', '/')
}
function Read-TextAuto([string]$Path) {
if (!(Test-Path $Path)) {
return ''
}
$bytes = [System.IO.File]::ReadAllBytes($Path)
if ($bytes.Length -eq 0) {
return ''
}
if ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFF -and $bytes[1] -eq 0xFE) {
return ([System.Text.Encoding]::Unicode.GetString($bytes, 2, $bytes.Length - 2) -replace "`0", '')
}
if ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFE -and $bytes[1] -eq 0xFF) {
return ([System.Text.Encoding]::BigEndianUnicode.GetString($bytes, 2, $bytes.Length - 2) -replace "`0", '')
}
if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) {
return ([System.Text.Encoding]::UTF8.GetString($bytes, 3, $bytes.Length - 3) -replace "`0", '')
}
for ($i = 1; $i -lt [Math]::Min($bytes.Length, 64); $i += 2) {
if ($bytes[$i] -eq 0) {
return ([System.Text.Encoding]::Unicode.GetString($bytes) -replace "`0", '')
}
}
return ([System.Text.Encoding]::UTF8.GetString($bytes) -replace "`0", '')
}
function Normalize-NativeText([string]$Text) {
if ([string]::IsNullOrEmpty($Text)) {
return ''
}
$value = $Text -replace "`0", ''
$value = $value -replace "`r`n", "`n"
$value = $value -replace "`r", "`n"
return $value
}
function Invoke-NativeCapture([string]$FilePath, [string[]]$ArgumentList) {
$stdoutPath = [System.IO.Path]::GetTempFileName()
$stderrPath = [System.IO.Path]::GetTempFileName()
try {
$proc = Start-Process -FilePath $FilePath -ArgumentList $ArgumentList -Wait -PassThru -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -WindowStyle Hidden
$stdoutText = if (Test-Path $stdoutPath) { Normalize-NativeText (Read-TextAuto $stdoutPath) } else { '' }
$stderrText = if (Test-Path $stderrPath) { Normalize-NativeText (Read-TextAuto $stderrPath) } else { '' }
$combined = (($stdoutText + "`n" + $stderrText).Trim())
return @{
ExitCode = $proc.ExitCode
StdOut = $stdoutText
StdErr = $stderrText
Combined = $combined
}
} finally {
Remove-Item -Force -ErrorAction SilentlyContinue $stdoutPath, $stderrPath
}
return $full.Replace('\', '/')
}
if ([string]::IsNullOrWhiteSpace($PackageDir)) {
@ -115,7 +43,7 @@ if ([string]::IsNullOrWhiteSpace($PluginCacheDir)) {
if ($env:PJARCZAK_BAMBU_WINDOWS_PLUGIN_CACHE_DIR) {
$PluginCacheDir = $env:PJARCZAK_BAMBU_WINDOWS_PLUGIN_CACHE_DIR
} elseif ($env:APPDATA) {
$PluginCacheDir = Join-Path $env:APPDATA 'OrcaSlicer\plugins'
$PluginCacheDir = Join-Path $env:APPDATA 'OrcaSlicer\ota'
}
}
if (-not [string]::IsNullOrWhiteSpace($PluginCacheDir)) {
@ -123,9 +51,13 @@ if (-not [string]::IsNullOrWhiteSpace($PluginCacheDir)) {
}
if ([string]::IsNullOrWhiteSpace($DistroName)) {
$distroFile = Join-Path $PackageDir 'pjarczak_wsl_distro.txt'
if (Test-Path $distroFile) {
$DistroName = (Get-Content $distroFile -Raw).Trim()
if ($env:PJARCZAK_WSL_DISTRO) {
$DistroName = $env:PJARCZAK_WSL_DISTRO.Trim()
} else {
$distroFile = Join-Path $PackageDir 'pjarczak_wsl_distro.txt'
if (Test-Path $distroFile) {
$DistroName = (Get-Content $distroFile -Raw).Trim()
}
}
}
if ([string]::IsNullOrWhiteSpace($DistroName)) {
@ -133,64 +65,47 @@ if ([string]::IsNullOrWhiteSpace($DistroName)) {
}
$requiredFiles = @(
'pjarczak_bambu_networking_bridge.dll',
'pjarczak_wsl_distro.txt',
'install_runtime.ps1',
'verify_runtime.ps1',
'pjarczak_wsl_run_host.sh',
'pjarczak_bambu_linux_host',
'pjarczak_bambu_linux_host_abi1',
'pjarczak_bambu_linux_host_abi0',
'windows-wsl2-rootfs.tar'
)
foreach ($name in $requiredFiles) {
$path = Join-Path $PackageDir $name
if (!(Test-Path $path)) {
throw "Missing package file: $name"
}
if (!(Test-Path $path)) { throw "Missing package file: $name" }
}
$bootstrapPath = Join-Path $PackageDir 'pjarczak_wsl_run_host.sh'
if (!(Test-Path $bootstrapPath)) { $bootstrapPath = Join-Path $PackageDir 'pjarczak-wsl-run-host.sh' }
if (!(Test-Path $bootstrapPath)) {
throw 'Missing package file: pjarczak_wsl_run_host.sh'
}
Convert-FileToLf $bootstrapPath
$wsl = Join-Path $env:WINDIR 'System32\wsl.exe'
if (!(Test-Path $wsl)) {
throw 'wsl.exe not found'
}
if (!(Test-Path $wsl)) { throw 'wsl.exe not found' }
$distroList = @()
$normalizedDistros = @()
for ($attempt = 0; $attempt -lt 10; $attempt++) {
$distroQuery = Invoke-NativeCapture $wsl @('-l', '-q')
if ($distroQuery.ExitCode -ne 0) {
throw "Failed to query installed WSL distros: $($distroQuery.Combined)"
}
$distroList = @()
if (-not [string]::IsNullOrWhiteSpace($distroQuery.StdOut)) {
$distroList = $distroQuery.StdOut -split "`n"
}
$normalizedDistros = $distroList | ForEach-Object { ($_ -replace "`0", '').Trim() } | Where-Object { $_ }
if ($normalizedDistros | Where-Object { $_ -eq $DistroName }) {
break
}
Start-Sleep -Milliseconds 400
}
if (-not ($normalizedDistros | Where-Object { $_ -eq $DistroName })) {
$distroList = & $wsl -l -q 2>$null
if ($LASTEXITCODE -ne 0) { throw 'Failed to query installed WSL distros' }
if (-not ($distroList | ForEach-Object { $_.Trim() } | Where-Object { $_ -eq $DistroName })) {
throw "WSL distro '$DistroName' is not installed"
}
$packageDirWsl = To-WslPath $PackageDir
$pluginCacheDirWsl = ""
$pluginCacheDirWsl = ''
if (-not [string]::IsNullOrWhiteSpace($PluginCacheDir)) {
$pluginCacheDirWsl = To-WslPath $PluginCacheDir
}
$bootstrapWsl = "$packageDirWsl/$([System.IO.Path]::GetFileName($bootstrapPath))"
$bootstrapWsl = "$packageDirWsl/pjarczak_wsl_run_host.sh"
$probe = Invoke-NativeCapture $wsl @('-d', $DistroName, '--user', 'root', '--', 'sh', $bootstrapWsl, '--probe', $packageDirWsl, $pluginCacheDirWsl)
if ($probe.ExitCode -ne 0) {
$probeText = $probe.Combined
if ($SkipProbe) {
Write-Host 'WSL runtime core OK'
exit 0
}
$probe = & $wsl -d $DistroName --user root -- sh $bootstrapWsl --probe $packageDirWsl $pluginCacheDirWsl 2>&1
if ($LASTEXITCODE -ne 0) {
$probeText = ($probe | Out-String).Trim()
if ($AllowMissingLinuxPlugin -and $probeText -match 'plugin_not_downloaded') {
Write-Host 'WSL runtime package OK, linux plugin not downloaded yet.'
Write-Host $probeText
@ -200,4 +115,4 @@ if ($probe.ExitCode -ne 0) {
}
Write-Host 'WSL runtime probe OK'
Write-Host $probe.Combined
Write-Host (($probe | Out-String).Trim())