mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-11-03 22:05:11 -05:00
* Load WASM extensions in the daemon and run their jobs with checkpoint and resume The plugin manager now keeps each extension's store and instance for its lifetime, discovers every manifest under <data dir>/extensions at startup before libraries open, and runs a registered job export on a blocking thread. The job host functions report into the running WasmJob through a bridge: progress, warnings, metrics, an interrupt flag the pause path sets, and checkpoints that land in the checkpoint table before the guest continues, so a pause or a kill resumes from the guest's last state. extensions.list and extensions.run_job expose the registry on the wire. The SDK's #[job] export calls the job function (async ones through a poll loop) and plugin_init installs a panic hook so a guest panic reaches the host log. wasmer moves to 6.1 because 4.4 no longer links on Rust 1.89+ (wasmer #5610). The test extension calls check_interrupt_sync, the name the SDK has for the synchronous check, and drops uuid's v4 feature it never used so it builds for wasm32-unknown-unknown again. The integration suite loads the committed fixture, runs the counter to completion, and pauses, restarts and resumes it; the whole integration group builds with the wasm feature so the suite shares one sd-core build. Co-authored-by: capy-ai-staging[bot] <305915985+capy-ai-staging[bot]@users.noreply.github.com> * Build and load the photos extension and stop at the first missing host function The photos extension builds for wasm32-unknown-unknown without wasm-bindgen imports: uuid and chrono drop their JS features, entropy is the unsupported getrandom backend until the SDK has a host function for it, and the FaceDetection glob ambiguity is resolved. Its four jobs get registered names and are listed in the extension macro, so plugin_init registers them. The committed photos.wasm fixture loads beside the test extension in the integration suite; its first job fails at JobContext::run, which has no host function, with the panic in the host log. Co-authored-by: capy-ai-staging[bot] <305915985+capy-ai-staging[bot]@users.noreply.github.com> * Keep guest progress messages, reload a trapped plugin, reload by directory Progress from a guest carried a message the host read and dropped; it now reaches the job as generic progress with the job name as its phase. A trap poisons the plugin runtime and the next job reloads the extension from disk, since an aborted guest may have died holding its allocator. reload_plugin reads the directory the plugin came from instead of assuming it equals the manifest id. Co-authored-by: capy-ai-staging[bot] <305915985+capy-ai-staging[bot]@users.noreply.github.com> * Probe a trapped plugin without its mutex and swap in the reload only on success The poison flag lives in an atomic beside the runtime, so the next job reads it under the manager's read lock instead of waiting on the mutex a running job holds, and the write lock is taken only for the reload. A reload reads, compiles and instantiates the new instance before the old one is unloaded, so a failed reload leaves the plugin as it was. A job that reached a poisoned instance fails instead of running on it. Co-authored-by: capy-ai-staging[bot] <305915985+capy-ai-staging[bot]@users.noreply.github.com> * Put the old plugin back when a reload's plugin_init fails; read mutex poison too A reload whose fresh instance fails plugin_init re-registers the old plugin's jobs and reinserts it, so one bad reload is one error rather than a missing extension. The poison probe also reads a poisoned runtime mutex, which a panic unwinding out of a guest call leaves behind without reaching the flag. Co-authored-by: capy-ai-staging[bot] <305915985+capy-ai-staging[bot]@users.noreply.github.com> * Run the WASM extension suite in the acceptance group with its own build wasmer in sd-core adds minutes to every test binary link, so carrying the wasm feature across the integration group took it from 51 to 78 minutes. The suite now builds alone with the feature in the acceptance job, which had 16 minutes to spare. Co-authored-by: capy-ai-staging[bot] <305915985+capy-ai-staging[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai-staging[bot] <305915985+capy-ai-staging[bot]@users.noreply.github.com>
103 lines
3.2 KiB
Plaintext
103 lines
3.2 KiB
Plaintext
---
|
|
title: Examples
|
|
description: A small durable job and a file-kind integration
|
|
sidebarTitle: Examples
|
|
---
|
|
|
|
## Durable counter
|
|
|
|
This extension registers one resumable job. Its serializable state is the
|
|
checkpoint.
|
|
|
|
~~~rust
|
|
use spacedrive_sdk::prelude::*;
|
|
|
|
#[extension(
|
|
id = "com.example.counter",
|
|
name = "Counter",
|
|
version = "0.1.0",
|
|
jobs = [count],
|
|
)]
|
|
struct CounterExtension;
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
pub struct CounterState {
|
|
pub current: u32,
|
|
pub target: u32,
|
|
}
|
|
|
|
#[job(name = "count")]
|
|
fn count(ctx: &JobContext, state: &mut CounterState) -> Result<()> {
|
|
while state.current < state.target {
|
|
if ctx.check_interrupt_sync() {
|
|
ctx.checkpoint(state)?;
|
|
return Err(Error::OperationFailed("Interrupted".into()));
|
|
}
|
|
|
|
state.current += 1;
|
|
ctx.report_progress(
|
|
state.current as f32 / state.target as f32,
|
|
&format!("Counted {}/{}", state.current, state.target),
|
|
);
|
|
|
|
if state.current % 10 == 0 {
|
|
ctx.checkpoint(state)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
~~~
|
|
|
|
The host persists progress, associates logs with the job ID, and resumes only
|
|
from committed state.
|
|
|
|
## Camera RAW support
|
|
|
|
A file-kind extension for a camera format contributes a small set of connected
|
|
capabilities:
|
|
|
|
1. Recognize the format from extension, MIME type, and magic bytes.
|
|
2. Extract camera, lens, exposure, dimensions, and embedded-preview metadata.
|
|
3. Register a fast preview using the embedded image.
|
|
4. Register a full renderer when the required codec is available.
|
|
5. Generate deterministic thumbnails and an optional editing proxy.
|
|
6. Add inspector rows and an “Open in registered editor” command.
|
|
|
|
The source keeps the original file record. Extracted facts record the extension
|
|
and input generation. Thumbnails remain reclaimable while the original and
|
|
generator are available. An explicitly retained proxy follows source policy.
|
|
|
|
If the extension is removed, Spacedrive still shows the file, path, content
|
|
identity, source, and generic metadata. It explains that the specialized preview
|
|
handler is missing.
|
|
|
|
## Photos library adapter
|
|
|
|
A Photos adapter reads an existing library as an origin:
|
|
|
|
- assets become standard source records;
|
|
- albums, edits, faces, and places become typed facets, models, and edges;
|
|
- original bytes stay where the photo library manages them;
|
|
- the user can choose a normal source as an export or backup destination.
|
|
|
|
Import clearly states that it will not move original files. The adapter uses
|
|
provider versions and identifiers to update the same records on later ingests.
|
|
|
|
The extension contributes People, Places, and Albums views over the common
|
|
operation API. An external agent can query those views through CLI or MCP
|
|
without receiving access to the extension database.
|
|
|
|
## Review checklist
|
|
|
|
Before shipping an extension:
|
|
|
|
- Build the WASM module reproducibly.
|
|
- Verify the package signature and stable ID.
|
|
- Review every requested capability.
|
|
- Test interruption and migration rollback.
|
|
- Confirm durable data survives reindex and uninstall.
|
|
- Confirm partial results when a source is offline.
|
|
- Confirm file mutation uses registered operations.
|
|
- Confirm the interface has a safe missing-extension fallback.
|