make example plugin slightly better to show examples

This commit is contained in:
KingKDot
2026-06-22 11:00:41 -05:00
parent 30dfdda3ed
commit 6180e7f1a2
6 changed files with 188 additions and 4 deletions
+1 -1
View File
@@ -333,7 +333,7 @@ Current Plugin 2.0 samples:
|--------|---------|
| `sample-wasm` | C/WASI file bridge sample using `pluginData`, `downloads`, and `overlord_host_info`. |
| `sample-wasm-hostinfo` | TinyGo sample that queries HostInfo from a universal WASM module. |
| `sample-wasm-platform-note` | Rust sample that branches by `os`/`arch` inside one WASM module and writes a platform-specific note. |
| `sample-wasm-platform-note` | Rust sample that branches by `os`/`arch`, writes a platform-specific note, and probes Desktop list/read access. |
| `sample-ts-fullstack` | TypeScript UI plus TypeScript server runtime with shared local modules. |
Legacy native samples remain available:
+3 -1
View File
@@ -1,6 +1,8 @@
# 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.
This Plugin 2.0 sample is written in Rust. It reads HostInfo, branches on `os` and `arch`, writes a small note into the approved `pluginData` bucket, then lists the user's Desktop and attempts to read a small sample from one regular file to prove the file bridge is working.
The manifest requests `pluginData` write/mkdir access and `desktop` list/read access, so changing this sample's permissions will require approving the new needs hash before load.
On Windows, install the Rust WASI target first:
@@ -6,6 +6,22 @@ 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_list(
bucket: *const u8,
bucket_len: i32,
path: *const u8,
path_len: i32,
out: *mut u8,
out_len: i32,
) -> i32;
fn overlord_fs_read(
bucket: *const u8,
bucket_len: i32,
path: *const u8,
path_len: i32,
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,
@@ -102,9 +118,14 @@ pub extern "C" fn overlord_on_event(
note.len() as i32,
)
};
let desktop = probe_desktop(host);
let response = format!(
r#"{{"os":"{os}","arch":"{arch}","mkdir":{mkdir_result},"write":{write_result}}}"#
r#"{{"os":"{os}","arch":"{arch}","mkdir":{mkdir_result},"write":{write_result},"desktopList":{},"desktopRead":{},"desktopFile":"{}","desktopSize":{}}}"#,
desktop.list_result,
desktop.read_result,
json_escape(&desktop.file),
desktop.size,
);
emit("platform_note", response.as_bytes());
0
@@ -124,6 +145,155 @@ fn emit(event: &str, payload: &[u8]) {
}
}
struct DesktopProbe {
file: String,
size: i64,
list_result: i32,
read_result: i32,
}
fn probe_desktop(seed: &str) -> DesktopProbe {
let bucket = "desktop";
let path = "";
let mut listing = Vec::new();
let mut list_result = -4;
for size in [8 * 1024, 64 * 1024, 256 * 1024, 1024 * 1024] {
listing.resize(size, 0);
list_result = unsafe {
overlord_fs_list(
bucket.as_ptr(),
bucket.len() as i32,
path.as_ptr(),
path.len() as i32,
listing.as_mut_ptr(),
listing.len() as i32,
)
};
if list_result != -4 {
break;
}
}
if list_result <= 0 {
return DesktopProbe {
file: String::new(),
size: -1,
list_result,
read_result: -3,
};
}
let listing = str::from_utf8(&listing[..list_result as usize]).unwrap_or("");
let files = desktop_files_from_listing(listing);
if files.is_empty() {
return DesktopProbe {
file: String::new(),
size: -1,
list_result,
read_result: -3,
};
}
let seed_sum = seed.bytes().fold(0usize, |acc, b| acc.wrapping_add(b as usize));
let selected = &files[seed_sum % files.len()];
let mut buf = [0u8; 128];
let read_result = unsafe {
overlord_fs_read(
bucket.as_ptr(),
bucket.len() as i32,
selected.path.as_ptr(),
selected.path.len() as i32,
buf.as_mut_ptr(),
buf.len() as i32,
)
};
DesktopProbe {
file: selected.path.clone(),
size: selected.size,
list_result,
read_result,
}
}
struct DesktopFile {
path: String,
size: i64,
}
fn desktop_files_from_listing(listing: &str) -> Vec<DesktopFile> {
let mut files = Vec::new();
let mut rest = listing;
while let Some(path_key) = rest.find(r#""path":"#) {
rest = &rest[path_key + 7..];
let Some((path, after_path)) = parse_json_string(rest) else {
break;
};
rest = after_path;
let next_path = rest.find(r#""path":"#).unwrap_or(rest.len());
let entry = &rest[..next_path];
if !entry.contains(r#""isDir":false"#) {
continue;
}
let size = parse_json_i64_after(entry, r#""size":"#).unwrap_or(-1);
files.push(DesktopFile { path, size });
}
files
}
fn parse_json_string(input: &str) -> Option<(String, &str)> {
let input = input.strip_prefix('"')?;
let mut out = String::new();
let mut escaped = false;
for (idx, ch) in input.char_indices() {
if escaped {
match ch {
'"' => out.push('"'),
'\\' => out.push('\\'),
'/' => out.push('/'),
'b' => out.push('\u{0008}'),
'f' => out.push('\u{000c}'),
'n' => out.push('\n'),
'r' => out.push('\r'),
't' => out.push('\t'),
other => out.push(other),
}
escaped = false;
continue;
}
match ch {
'\\' => escaped = true,
'"' => return Some((out, &input[idx + 1..])),
other => out.push(other),
}
}
None
}
fn parse_json_i64_after(input: &str, key: &str) -> Option<i64> {
let start = input.find(key)? + key.len();
let bytes = input.as_bytes();
let mut end = start;
while end < bytes.len() && (bytes[end].is_ascii_digit() || bytes[end] == b'-') {
end += 1;
}
input[start..end].parse().ok()
}
fn json_escape(input: &str) -> String {
let mut out = String::with_capacity(input.len());
for ch in input.chars() {
match ch {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
other => out.push(other),
}
}
out
}
#[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) };
@@ -1 +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":{}}
{"rustc_fingerprint":2568544898921049753,"outputs":{"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":""},"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"}},"successes":{}}
@@ -7,6 +7,10 @@
<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 class="sample-wasm-platform-note-field"><span>Desktop file</span><strong id="sample-wasm-platform-note-desktop-file">-</strong></div>
<div class="sample-wasm-platform-note-field"><span>Desktop size</span><strong id="sample-wasm-platform-note-desktop-size">-</strong></div>
<div class="sample-wasm-platform-note-field"><span>Desktop list</span><strong id="sample-wasm-platform-note-desktop-list">-</strong></div>
<div class="sample-wasm-platform-note-field"><span>Desktop read</span><strong id="sample-wasm-platform-note-desktop-read">-</strong></div>
</div>
<pre id="sample-wasm-platform-note-output" class="text-xs text-slate-300 whitespace-pre-wrap"></pre>
</div>
@@ -7,6 +7,10 @@
arch: document.getElementById("sample-wasm-platform-note-arch"),
mkdir: document.getElementById("sample-wasm-platform-note-mkdir"),
write: document.getElementById("sample-wasm-platform-note-write"),
desktopFile: document.getElementById("sample-wasm-platform-note-desktop-file"),
desktopSize: document.getElementById("sample-wasm-platform-note-desktop-size"),
desktopList: document.getElementById("sample-wasm-platform-note-desktop-list"),
desktopRead: document.getElementById("sample-wasm-platform-note-desktop-read"),
};
const clientId = new URLSearchParams(window.location.search).get("clientId");
const pluginId = "sample-wasm-platform-note";
@@ -34,6 +38,10 @@
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 ?? "-");
if (fields.desktopFile) fields.desktopFile.textContent = info.desktopFile || "(no readable file found)";
if (fields.desktopSize) fields.desktopSize.textContent = Number.isFinite(info.desktopSize) && info.desktopSize >= 0 ? `${info.desktopSize} bytes` : "-";
if (fields.desktopList) fields.desktopList.textContent = String(info.desktopList ?? "-");
if (fields.desktopRead) fields.desktopRead.textContent = String(info.desktopRead ?? "-");
setStatus(info.write === 0 ? "Platform note written" : "Platform note result received");
log(JSON.stringify(info, null, 2));
}