Skip to content

Commit 7c2bb11

Browse files
committed
fix: resolve Linux CI cargo targets, permission errors, and implement headless GTK stdin loop
1 parent 644b58d commit 7c2bb11

8 files changed

Lines changed: 65 additions & 35 deletions

File tree

plug.app/rt/.cargo/config.toml

Lines changed: 0 additions & 4 deletions
This file was deleted.

plug.app/rt/handlers/ops/cmds/c_plug/check/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ fn compare_versions(registry: &str, local: &str) -> bool {
3333
}
3434

3535
pub fn download_file_hr(url: &str, dest: &str) -> i32 {
36-
let req = ureq::get(url).set("Cache-Control", "no-cache");
36+
let req = ureq::get(url)
37+
.set("Cache-Control", "no-cache")
38+
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
3739
match req.call() {
3840
Ok(res) => {
3941
if let Ok(mut file) = fs::File::create(dest) {

plug.app/rt/handlers/ops/cmds/c_plug/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub fn install_plugin_manually(clean_name: &str, session_hash: &str, registry_sh
9797
if let Ok(_) = fs::copy(&local_wasm, &tmp_wasm) {
9898
downloaded_wasm = true;
9999
if !silent {
100-
print_info(&format!("local fallback: copy plugin from {}", local_wasm.display()));
100+
print_info(&format!("local fallback: copy plugin: {}", clean_name));
101101
}
102102
}
103103
}
@@ -117,7 +117,7 @@ pub fn install_plugin_manually(clean_name: &str, session_hash: &str, registry_sh
117117
if let Ok(_) = fs::copy(&local_toml, &tmp_toml) {
118118
downloaded_toml = true;
119119
if !silent {
120-
print_info(&format!("local fallback: copy manifest from {}", local_toml.display()));
120+
print_info("local fallback: copy manifest");
121121
}
122122
}
123123
}
@@ -137,7 +137,7 @@ pub fn install_plugin_manually(clean_name: &str, session_hash: &str, registry_sh
137137

138138
let sha256_hex = crate::ops::utils::calculate_buffer_sha256(&wasm_bytes);
139139

140-
// phase 2.3: verify against registry-pinned hash (fail-closed on missing or mismatch)
140+
// verify against registry-pinned hash (fail-closed on missing or mismatch)
141141
if registry_sha256.is_empty() {
142142
print_error(&format!("[SECURITY] Missing pinned hash in registry for plugin: {}. Aborting installation.", clean_name));
143143
let _ = fs::remove_file(&tmp_wasm);

plug.app/rt/handlers/ops/cmds/c_plug/progress.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::time::{Instant, Duration};
44
use crate::ops::host::print_info;
55

66
pub fn download_with_progress(url: &str, dest_path: &str, silent: bool) -> bool {
7-
let agent = "plug-agent";
7+
let agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
88
let req = ureq::get(url).set("User-Agent", agent);
99
let response = match req.call() {
1010
Ok(res) => res,

plug.app/rt/handlers/ops/net.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,6 @@ use serde_json::Value;
66
const DEFAULT_TIMEOUT: u64 = 30;
77
const USER_AGENT: &str = "plug-runtime/1.0.0";
88

9-
/// execute get reqest and return raw string
10-
pub fn http_get(url: &str) -> Result<String, String> {
11-
let agent = ureq::AgentBuilder::new()
12-
.timeout(Duration::from_secs(DEFAULT_TIMEOUT))
13-
.build();
14-
15-
let response = agent.get(url)
16-
.set("User-Agent", USER_AGENT)
17-
.call()
18-
.map_err(|e| format!("GET failed: {}", e))?;
19-
20-
response.into_string()
21-
.map_err(|e| format!("Failed to read response body: {}", e))
22-
}
23-
249
/// execute post reqest with json body
2510
pub fn http_post_json(url: &str, json_payload: &str) -> Result<String, String> {
2611
// check input json format validity
@@ -40,12 +25,3 @@ pub fn http_post_json(url: &str, json_payload: &str) -> Result<String, String> {
4025
response.into_string()
4126
.map_err(|e| format!("Failed to read response body: {}", e))
4227
}
43-
44-
/// check if internet is online
45-
pub fn is_online() -> bool {
46-
// quick check using google dns
47-
ureq::get("http://clients3.google.com/generate_204")
48-
.timeout(Duration::from_secs(3))
49-
.call()
50-
.is_ok()
51-
}

plug.app/src/linux/api/intf/main_l.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,56 @@ std::vector<TabState> g_tabs;
3737
int g_active_tab = 0;
3838
std::mutex g_mutex;
3939

40+
extern "C" bool g_headless_mode;
4041
static std::thread g_cmd_thread;
4142
static std::mutex g_cmd_mutex;
4243
static std::condition_variable g_cmd_cv;
4344
struct CmdItem { int tab; std::string cmd; };
4445
static std::queue<CmdItem> g_cmd_queue;
4546
static std::atomic_bool g_cmd_thread_running{false};
47+
static std::thread g_headless_stdin_thread;
48+
49+
static void headless_stdin_worker(void) {
50+
std::string line;
51+
while (std::getline(std::cin, line)) {
52+
if (line.empty()) continue;
53+
54+
if (line == "__dump_state__") {
55+
std::lock_guard<std::mutex> lk(g_mutex);
56+
std::cout << "\n---STATE_DUMP_START---\n";
57+
std::cout << "{\n";
58+
std::cout << " \"active_tab\": " << g_active_tab << ",\n";
59+
std::cout << " \"tabs\": [\n";
60+
for (size_t i = 0; i < g_tabs.size(); ++i) {
61+
std::string title_utf8 = g_tabs[i].title;
62+
std::string raw_input = g_tabs[i].input_buffer;
63+
std::string escaped_input;
64+
for (char c : raw_input) {
65+
if (c == '\\') escaped_input += "\\\\";
66+
else if (c == '"') escaped_input += "\\\"";
67+
else escaped_input += c;
68+
}
69+
std::cout << " {\n";
70+
std::cout << " \"index\": " << i << ",\n";
71+
std::cout << " \"title\": \"" << title_utf8 << "\",\n";
72+
std::cout << " \"input_buffer\": \"" << escaped_input << "\",\n";
73+
std::cout << " \"plugin_owner\": \"" << (g_tabs[i].plugin_owner.empty() ? "" : g_tabs[i].plugin_owner) << "\"\n";
74+
std::cout << " }" << (i + 1 < g_tabs.size() ? "," : "") << "\n";
75+
}
76+
std::cout << " ]\n";
77+
std::cout << "}\n";
78+
std::cout << "---STATE_DUMP_END---\n" << std::endl;
79+
continue;
80+
}
81+
82+
// push command to queue
83+
{
84+
std::lock_guard<std::mutex> clk(g_cmd_mutex);
85+
g_cmd_queue.push({g_active_tab, line});
86+
g_cmd_cv.notify_one();
87+
}
88+
}
89+
}
4690
thread_local int g_print_tab = -1;
4791
int g_hover_close_tab = -1;
4892
std::vector<HitBox> g_tab_close_boxes;
@@ -429,7 +473,7 @@ static void on_drag_end(GtkGestureDrag *gesture, double offset_x, double offset_
429473
}
430474

431475
static gboolean on_tick(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer user_data) {
432-
// Check if command thread is active (pseudo-logic for now to match Windows)
476+
// check if command thread is active
433477
if (!g_cmd_thread_running) {
434478
g_cmd_thread_running = true;
435479
g_cmd_thread = std::thread([]() {
@@ -450,6 +494,9 @@ static gboolean on_tick(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer
450494
}
451495
}
452496
});
497+
if (g_headless_mode) {
498+
g_headless_stdin_thread = std::thread(headless_stdin_worker);
499+
}
453500
}
454501

455502
{
@@ -546,6 +593,9 @@ void main_l_cleanup(void) {
546593
g_object_unref(g_app);
547594
g_app = NULL;
548595
}
596+
if (g_headless_stdin_thread.joinable()) {
597+
g_headless_stdin_thread.detach();
598+
}
549599
}
550600

551601
void main_l_print_info(const char* msg) {

plug.cross/linux/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ project(plug LANGUAGES C CXX)
44
set(CMAKE_CXX_STANDARD 17)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)
66

7+
option(PLUG_ENABLE_HEADLESS_MODE "Enable headless mode for E2E testing" OFF)
8+
if(PLUG_ENABLE_HEADLESS_MODE)
9+
add_compile_definitions(PLUG_ENABLE_HEADLESS_MODE)
10+
endif()
11+
712
set(ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..")
813
set(APP_DIR "${ROOT_DIR}/plug.app")
914
set(COMMON_DIR "${APP_DIR}/cmn")

tests/integration/test_sandbox_rules.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ def main():
8080
sys_drive += "\\"
8181
plug_dir = Path(sys_drive) / ".plug" / "plugins"
8282
else:
83-
plug_dir = Path("/.plug/plugins")
83+
# on linux systemdrive is not set so app default to c:\
84+
plug_dir = Path("C:\\") / ".plug" / "plugins"
8485

8586
# backup existing plugin folder if it exists
8687
backup_dir = plug_dir.parent / "plugins_backup"

0 commit comments

Comments
 (0)