plugins 2.0 update

This commit is contained in:
KingKDot
2026-06-22 10:52:27 -05:00
parent e1f5e4c800
commit 30dfdda3ed
55 changed files with 2534 additions and 128 deletions
@@ -0,0 +1,11 @@
# Sample WASM Platform Note Plugin
This Plugin 2.0 sample is written in Rust. It reads HostInfo, branches on `os` and `arch`, and writes a small note into the approved `pluginData` bucket.
On Windows, install the Rust WASI target first:
```bat
rustup target add wasm32-wasip1
```
Then run this sample's local `build.bat`. The builder runs Cargo, writes `sample-wasm-platform-note.wasm`, and creates `sample-wasm-platform-note.zip`.
@@ -0,0 +1,76 @@
@echo off
setlocal enabledelayedexpansion
set "PLUGIN_DIR=%~dp0"
set "PLUGIN_NAME=sample-wasm-platform-note"
set "NATIVE_DIR=%PLUGIN_DIR%\native"
set "WASM_OUT=%PLUGIN_DIR%\%PLUGIN_NAME%.wasm"
set "ZIP_OUT=%PLUGIN_DIR%\%PLUGIN_NAME%.zip"
if not exist "%PLUGIN_DIR%\config.json" (
echo [error] config.json not found in %PLUGIN_DIR%
exit /b 1
)
set "RUST_MANIFEST=%NATIVE_DIR%\Cargo.toml"
if exist "%RUST_MANIFEST%" (
where cargo >nul 2>&1
if errorlevel 1 (
echo [error] cargo was not found. Install Rust or provide %WASM_OUT% yourself.
exit /b 1
)
where rustup >nul 2>&1
if errorlevel 1 (
echo [error] rustup was not found. Install the Rust target manually: rustup target add wasm32-wasip1
exit /b 1
)
rustup target list --installed | findstr /x "wasm32-wasip1" >nul 2>&1
if errorlevel 1 (
echo [setup] Installing Rust target wasm32-wasip1
rustup target add wasm32-wasip1
if errorlevel 1 (
echo [error] Could not install wasm32-wasip1. Run this manually: rustup target add wasm32-wasip1
exit /b 1
)
)
echo [build] cargo build --manifest-path "%RUST_MANIFEST%" --release --target wasm32-wasip1
cargo build --manifest-path "%RUST_MANIFEST%" --release --target wasm32-wasip1
if errorlevel 1 (
echo [error] WASM build failed
exit /b 1
)
copy /Y "%NATIVE_DIR%\target\wasm32-wasip1\release\sample_wasm_platform_note.wasm" "%WASM_OUT%" >nul
if errorlevel 1 (
echo [error] could not copy built WASM to %WASM_OUT%
exit /b 1
)
)
if exist "%ZIP_OUT%" del /f /q "%ZIP_OUT%"
pushd "%PLUGIN_DIR%"
powershell -NoProfile -ExecutionPolicy Bypass -Command "$zip = '%ZIP_OUT%'; $items = @(); foreach ($file in @('config.json', '%PLUGIN_NAME%.html', '%PLUGIN_NAME%.css', '%PLUGIN_NAME%.js', '%PLUGIN_NAME%.wasm', 'server.js')) { if (Test-Path -LiteralPath $file -PathType Leaf) { $items += $file } }; foreach ($dir in @('src', 'assets')) { if (Test-Path -LiteralPath $dir -PathType Container) { $items += $dir } }; if ($items.Count -eq 0) { throw 'No plugin files found to package' }; Compress-Archive -Path $items -DestinationPath $zip -Force"
set "ZIP_STATUS=%ERRORLEVEL%"
popd
if not "%ZIP_STATUS%"=="0" exit /b %ZIP_STATUS%
if errorlevel 1 (
echo [error] zip failed
exit /b 1
)
if defined PLUGIN_SIGN_KEY (
where bun >nul 2>&1
if not errorlevel 1 (
echo [sign] Signing plugin with key: %PLUGIN_SIGN_KEY%
bun run "%~dp0..\..\Overlord-Server\scripts\plugin-sign.ts" --key "%PLUGIN_SIGN_KEY%" "%ZIP_OUT%"
) else (
echo [warn] bun not found, skipping plugin signing
)
)
echo [ok] %ZIP_OUT%
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "sample_wasm_platform_note"
version = "0.1.0"
@@ -0,0 +1,8 @@
[package]
name = "sample_wasm_platform_note"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
path = "src/lib.rs"
@@ -0,0 +1,131 @@
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;
use std::slice;
use std::str;
extern "C" {
fn overlord_emit(event: *const u8, event_len: i32, payload: *const u8, payload_len: i32) -> i32;
fn overlord_host_info(out: *mut u8, out_len: i32) -> i32;
fn overlord_fs_mkdir(bucket: *const u8, bucket_len: i32, path: *const u8, path_len: i32) -> i32;
fn overlord_fs_write(
bucket: *const u8,
bucket_len: i32,
path: *const u8,
path_len: i32,
data: *const u8,
data_len: i32,
) -> i32;
}
#[no_mangle]
pub extern "C" fn overlord_alloc(size: u32) -> *mut u8 {
let size = size.max(1) as usize;
let layout = Layout::from_size_align(size, 8).unwrap();
unsafe { alloc(layout) }
}
#[no_mangle]
pub extern "C" fn overlord_free(ptr: *mut u8, size: u32) {
if ptr.is_null() {
return;
}
let layout = Layout::from_size_align((size.max(1)) as usize, 8).unwrap();
unsafe { dealloc(ptr, layout) };
}
#[no_mangle]
pub extern "C" fn overlord_on_load(_host: *const u8, _host_len: u32) -> i32 {
emit("ready", br#"{"sample":"platform-note"}"#);
0
}
#[no_mangle]
pub extern "C" fn overlord_on_event(
event: *const u8,
event_len: u32,
_payload: *const u8,
_payload_len: u32,
) -> i32 {
let event = unsafe { slice::from_raw_parts(event, event_len as usize) };
if event != b"write_note" {
return 0;
}
let mut host = [0u8; 1024];
let host_len = unsafe { overlord_host_info(host.as_mut_ptr(), host.len() as i32) };
let host = if host_len > 0 {
str::from_utf8(&host[..host_len as usize]).unwrap_or("")
} else {
""
};
let os = if host.contains(r#""os":"windows""#) {
"windows"
} else if host.contains(r#""os":"darwin""#) {
"darwin"
} else if host.contains(r#""os":"linux""#) {
"linux"
} else {
"unknown"
};
let arch = if host.contains(r#""arch":"arm64""#) {
"arm64"
} else if host.contains(r#""arch":"amd64""#) {
"amd64"
} else if host.contains(r#""arch":"386""#) {
"386"
} else {
"unknown"
};
let bucket = "pluginData";
let dir = "platform";
let file = format!("platform/{os}-{arch}.txt");
let note = format!("single WASM plugin running on os={os} arch={arch}\n");
let mkdir_result = unsafe {
overlord_fs_mkdir(
bucket.as_ptr(),
bucket.len() as i32,
dir.as_ptr(),
dir.len() as i32,
)
};
let write_result = unsafe {
overlord_fs_write(
bucket.as_ptr(),
bucket.len() as i32,
file.as_ptr(),
file.len() as i32,
note.as_ptr(),
note.len() as i32,
)
};
let response = format!(
r#"{{"os":"{os}","arch":"{arch}","mkdir":{mkdir_result},"write":{write_result}}}"#
);
emit("platform_note", response.as_bytes());
0
}
#[no_mangle]
pub extern "C" fn overlord_on_unload() {}
fn emit(event: &str, payload: &[u8]) {
unsafe {
overlord_emit(
event.as_ptr(),
event.len() as i32,
payload.as_ptr(),
payload.len() as i32,
);
}
}
#[no_mangle]
pub extern "C" fn memset(dest: *mut u8, value: i32, len: usize) -> *mut u8 {
unsafe { ptr::write_bytes(dest, value as u8, len) };
dest
}
@@ -0,0 +1 @@
{"rustc_fingerprint":2568544898921049753,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\this1\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""},"5946400966674136012":{"success":true,"status":"","code":0,"stdout":"___.wasm\nlib___.rlib\n___.wasm\nlib___.a\nC:\\Users\\this1\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\noff\n___\ndebug_assertions\npanic=\"abort\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"wasm32\"\ntarget_endian=\"little\"\ntarget_env=\"p1\"\ntarget_family=\"wasm\"\ntarget_feature=\"bulk-memory\"\ntarget_feature=\"multivalue\"\ntarget_feature=\"mutable-globals\"\ntarget_feature=\"nontrapping-fptoint\"\ntarget_feature=\"reference-types\"\ntarget_feature=\"sign-ext\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"wasi\"\ntarget_pointer_width=\"32\"\ntarget_vendor=\"unknown\"\n","stderr":"warning: dropping unsupported crate type `dylib` for target `wasm32-wasip1`\n\nwarning: dropping unsupported crate type `proc-macro` for target `wasm32-wasip1`\n\nwarning: 2 warnings emitted\n\n"},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.94.0 (4a4ef493e 2026-03-02)\nbinary: rustc\ncommit-hash: 4a4ef493e3a1488c6e321570238084b38948f6db\ncommit-date: 2026-03-02\nhost: x86_64-pc-windows-msvc\nrelease: 1.94.0\nLLVM version: 21.1.8\n","stderr":""}},"successes":{}}
@@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/
@@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/
@@ -0,0 +1,22 @@
#sample-wasm-platform-note-output {
min-height: 8rem;
}
.sample-wasm-platform-note-field {
border: 1px solid rgba(5, 150, 105, 0.45);
background: rgba(6, 78, 59, 0.25);
padding: 0.75rem;
}
.sample-wasm-platform-note-field span {
display: block;
color: rgb(148, 163, 184);
font-size: 0.75rem;
}
.sample-wasm-platform-note-field strong {
display: block;
color: rgb(209, 250, 229);
font-size: 0.95rem;
overflow-wrap: anywhere;
}
@@ -0,0 +1,12 @@
<div class="space-y-3">
<h2 class="text-lg font-semibold text-slate-100">Sample WASM Platform Note</h2>
<button id="sample-wasm-platform-note-run" class="px-3 py-2 rounded-lg border border-emerald-700 bg-emerald-950/40 text-emerald-100">Write platform note</button>
<div id="sample-wasm-platform-note-status" class="text-xs text-slate-400">Waiting to write platform note</div>
<div class="grid gap-2 sm:grid-cols-2">
<div class="sample-wasm-platform-note-field"><span>OS</span><strong id="sample-wasm-platform-note-os">-</strong></div>
<div class="sample-wasm-platform-note-field"><span>Arch</span><strong id="sample-wasm-platform-note-arch">-</strong></div>
<div class="sample-wasm-platform-note-field"><span>mkdir</span><strong id="sample-wasm-platform-note-mkdir">-</strong></div>
<div class="sample-wasm-platform-note-field"><span>write</span><strong id="sample-wasm-platform-note-write">-</strong></div>
</div>
<pre id="sample-wasm-platform-note-output" class="text-xs text-slate-300 whitespace-pre-wrap"></pre>
</div>
@@ -0,0 +1,93 @@
(() => {
const out = document.getElementById("sample-wasm-platform-note-output");
const btn = document.getElementById("sample-wasm-platform-note-run");
const status = document.getElementById("sample-wasm-platform-note-status");
const fields = {
os: document.getElementById("sample-wasm-platform-note-os"),
arch: document.getElementById("sample-wasm-platform-note-arch"),
mkdir: document.getElementById("sample-wasm-platform-note-mkdir"),
write: document.getElementById("sample-wasm-platform-note-write"),
};
const clientId = new URLSearchParams(window.location.search).get("clientId");
const pluginId = "sample-wasm-platform-note";
let polling = false;
function log(message) {
if (out) out.textContent = `${message}\n${out.textContent || ""}`;
}
function setStatus(message) {
if (status) status.textContent = message;
}
function parsePayload(payload) {
if (payload == null) return {};
if (typeof payload === "string") {
try { return JSON.parse(payload); } catch (_) { return { raw: payload }; }
}
return payload;
}
function renderPlatformNote(payload) {
const info = parsePayload(payload);
if (fields.os) fields.os.textContent = info.os || "-";
if (fields.arch) fields.arch.textContent = info.arch || "-";
if (fields.mkdir) fields.mkdir.textContent = String(info.mkdir ?? "-");
if (fields.write) fields.write.textContent = String(info.write ?? "-");
setStatus(info.write === 0 ? "Platform note written" : "Platform note result received");
log(JSON.stringify(info, null, 2));
}
function handlePluginEvent(event, payload) {
if (event === "ready") {
setStatus("Plugin loaded");
log(JSON.stringify(parsePayload(payload), null, 2));
return;
}
if (event === "platform_note") renderPlatformNote(payload);
}
window.addEventListener("message", (e) => {
if (!e.data || e.data.type !== "plugin_event") return;
if (e.data.pluginId && e.data.pluginId !== pluginId) return;
handlePluginEvent(e.data.event, e.data.payload);
});
async function pollEvents() {
if (!clientId || polling) return;
polling = true;
try {
const res = await fetch(`/api/clients/${encodeURIComponent(clientId)}/plugins/${pluginId}/events`, {
headers: { Accept: "application/json" },
});
if (res.ok) {
const data = await res.json();
if (data.events) data.events.forEach((e) => handlePluginEvent(e.event, e.payload));
}
} catch (_) {
// Polling is best-effort; the next interval can recover.
} finally {
polling = false;
}
}
btn?.addEventListener("click", async () => {
if (!clientId) {
setStatus("Open this plugin from a client row so clientId is present.");
return;
}
setStatus("Writing platform note");
const res = await fetch(`/api/clients/${encodeURIComponent(clientId)}/plugins/sample-wasm-platform-note/event`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ event: "write_note", payload: {} }),
});
if (!res.ok) setStatus(`Send failed: ${res.status}`);
setTimeout(pollEvents, 250);
});
if (clientId) {
setInterval(pollEvents, 1500);
setTimeout(pollEvents, 300);
}
})();