feat: reject legacy plugin runtime manifests
This commit is contained in:
+69
-192
@@ -560,13 +560,14 @@ impl PluginPackageManifest {
|
||||
}
|
||||
}
|
||||
|
||||
pub const PLUGIN_RUNTIME_WASM_KIND: &str = "wasm";
|
||||
pub const PLUGIN_RUNTIME_WASM_ABI: &str = "yoi-plugin-wasm-1";
|
||||
/// Manifest runtime kind for WebAssembly Component Model Tool packages.
|
||||
const LEGACY_PLUGIN_RUNTIME_WASM_KIND: &str = "wasm";
|
||||
const LEGACY_PLUGIN_RUNTIME_WASM_ABI: &str = "yoi-plugin-wasm-1";
|
||||
/// Manifest runtime kind for current WebAssembly Component Model packages.
|
||||
///
|
||||
/// Component runtime manifests must set `component` to the packaged component
|
||||
/// artifact path and `world` to [`PLUGIN_COMPONENT_TOOL_WORLD`]. Raw core-Wasm
|
||||
/// packages remain explicit `kind = "wasm"` plus `abi = "yoi-plugin-wasm-1"`.
|
||||
/// artifact path and `world` to [`PLUGIN_COMPONENT_TOOL_WORLD`] or
|
||||
/// [`PLUGIN_COMPONENT_INSTANCE_WORLD`]. Legacy raw core-Wasm manifests are
|
||||
/// intentionally rejected by validation; `wasm-component` is the public runtime.
|
||||
pub const PLUGIN_RUNTIME_COMPONENT_KIND: &str = "wasm-component";
|
||||
pub const PLUGIN_COMPONENT_TOOL_WORLD: &str = "yoi:plugin/tool@1.0.0";
|
||||
pub const PLUGIN_COMPONENT_INSTANCE_WORLD: &str = "yoi:plugin/instance@1.0.0";
|
||||
@@ -1060,158 +1061,6 @@ pub fn resolve_plugin_config_for_startup(
|
||||
snapshot
|
||||
}
|
||||
|
||||
/// Load the recorded WASM runtime module for a resolved plugin package.
|
||||
///
|
||||
/// Restore and execution paths use this helper instead of reading arbitrary
|
||||
/// package paths directly so module selection remains tied to the resolved
|
||||
/// package identity, runtime manifest entry, and deterministic package digest.
|
||||
pub fn read_resolved_plugin_runtime_module(
|
||||
record: &ResolvedPluginRecord,
|
||||
limits: &PluginDiscoveryLimits,
|
||||
) -> Result<Vec<u8>, PluginDiagnostic> {
|
||||
let runtime = record.manifest.runtime.as_ref().ok_or_else(|| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Missing,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"resolved plugin package does not declare a WASM runtime",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})?;
|
||||
|
||||
if runtime.kind != PLUGIN_RUNTIME_WASM_KIND {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin runtime kind is unsupported",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest));
|
||||
}
|
||||
if runtime.abi.as_deref() != Some(PLUGIN_RUNTIME_WASM_ABI) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin WASM ABI is unsupported",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest));
|
||||
}
|
||||
|
||||
let entry = runtime.entry.as_deref().ok_or_else(|| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Missing,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin WASM runtime entry is required",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})?;
|
||||
|
||||
let metadata = fs::metadata(&record.package_path).map_err(|error| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Io,
|
||||
PluginDiagnosticPhase::Discovery,
|
||||
format!(
|
||||
"resolved plugin package metadata could not be read: {}",
|
||||
safe_io_error(&error)
|
||||
),
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})?;
|
||||
if !metadata.is_file() {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Malformed,
|
||||
PluginDiagnosticPhase::Discovery,
|
||||
"resolved plugin package is not a regular file",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest));
|
||||
}
|
||||
if metadata.len() > limits.max_package_size_bytes {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Bounds,
|
||||
PluginDiagnosticPhase::Discovery,
|
||||
"resolved plugin package exceeds the configured package size bound",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest));
|
||||
}
|
||||
|
||||
let bytes = fs::read(&record.package_path).map_err(|error| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Io,
|
||||
PluginDiagnosticPhase::Discovery,
|
||||
format!(
|
||||
"resolved plugin package content could not be read: {}",
|
||||
safe_io_error(&error)
|
||||
),
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})?;
|
||||
let archive = parse_stored_zip(&bytes, &record.package_label, record.source, limits)?;
|
||||
let actual_digest = deterministic_digest(&archive.files);
|
||||
if !digest_matches(&record.digest, &actual_digest) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Digest,
|
||||
PluginDiagnosticPhase::Resolution,
|
||||
"resolved plugin package digest does not match current package content",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(actual_digest));
|
||||
}
|
||||
|
||||
validate_manifest_path(
|
||||
entry,
|
||||
&archive,
|
||||
&record.package_label,
|
||||
record.source,
|
||||
&record.manifest.id,
|
||||
)?;
|
||||
let normalized = normalize_archive_path(entry).ok_or_else(|| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Traversal,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin manifest references a path outside the package root",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})?;
|
||||
archive.files.get(&normalized).cloned().ok_or_else(|| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Missing,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin runtime module entry is missing from the package",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads the WebAssembly Component Model artifact selected by a resolved plugin
|
||||
/// package manifest while preserving package digest pinning.
|
||||
pub fn read_resolved_plugin_runtime_component(
|
||||
@@ -2046,38 +1895,21 @@ fn validate_manifest(
|
||||
}
|
||||
if let Some(runtime) = &manifest.runtime {
|
||||
match runtime.kind.as_str() {
|
||||
PLUGIN_RUNTIME_WASM_KIND => {
|
||||
if runtime.abi.as_deref() != Some(PLUGIN_RUNTIME_WASM_ABI) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin WASM ABI is unsupported",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
let Some(entry) = runtime.entry.as_deref() else {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Missing,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin WASM runtime entry is required",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
};
|
||||
if runtime.component.is_some() || runtime.world.is_some() {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Malformed,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin WASM runtime must not declare component metadata",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
validate_manifest_path(entry, archive, label, source, &manifest.id)?;
|
||||
LEGACY_PLUGIN_RUNTIME_WASM_KIND => {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
format!(
|
||||
"legacy raw wasm plugin runtime `{LEGACY_PLUGIN_RUNTIME_WASM_KIND}` / `{}` is retired; use `{PLUGIN_RUNTIME_COMPONENT_KIND}`",
|
||||
runtime
|
||||
.abi
|
||||
.as_deref()
|
||||
.unwrap_or(LEGACY_PLUGIN_RUNTIME_WASM_ABI)
|
||||
),
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
PLUGIN_RUNTIME_COMPONENT_KIND => {
|
||||
if runtime.abi.is_some() || runtime.entry.is_some() {
|
||||
@@ -2844,6 +2676,51 @@ description = "bad"
|
||||
assert!(err.message.contains("service/ingress"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_raw_wasm_runtime_manifest_is_rejected() {
|
||||
let temp = TempDir::new().unwrap();
|
||||
let workspace = temp.path().join("workspace");
|
||||
let plugins = workspace.join(".yoi/plugins");
|
||||
fs::create_dir_all(&plugins).unwrap();
|
||||
let manifest = r#"
|
||||
schema_version = 1
|
||||
id = "legacy"
|
||||
name = "Legacy"
|
||||
version = "0.1.0"
|
||||
surfaces = ["tool"]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
|
||||
[[tools]]
|
||||
name = "Echo"
|
||||
description = "legacy"
|
||||
input_schema = { type = "object" }
|
||||
"#;
|
||||
write_stored_zip(
|
||||
&plugins.join("legacy.yoi-plugin"),
|
||||
&[
|
||||
("plugin.toml", manifest.as_bytes().to_vec(), 0),
|
||||
("plugin.wasm", b"not wasm".to_vec(), 0),
|
||||
],
|
||||
);
|
||||
|
||||
let report = discover_plugins(&PluginDiscoveryOptions::new(&workspace));
|
||||
|
||||
assert!(report.packages.is_empty());
|
||||
let diagnostic = report
|
||||
.diagnostics
|
||||
.iter()
|
||||
.find(|diag| diag.kind == PluginDiagnosticKind::Api)
|
||||
.unwrap();
|
||||
assert_eq!(diagnostic.phase, PluginDiagnosticPhase::Manifest);
|
||||
assert_eq!(diagnostic.identity.as_deref(), Some("project:legacy"));
|
||||
assert!(diagnostic.message.contains("legacy raw wasm"));
|
||||
assert!(diagnostic.message.contains(PLUGIN_RUNTIME_COMPONENT_KIND));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discovers_valid_user_and_workspace_packages() {
|
||||
let temp = TempDir::new().unwrap();
|
||||
@@ -3521,9 +3398,9 @@ version = "1.0.0"
|
||||
surfaces = ["tool"]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
|
||||
[[permissions]]
|
||||
kind = "host_api"
|
||||
|
||||
@@ -22,10 +22,9 @@ use llm_worker::tool::{
|
||||
};
|
||||
use manifest::plugin::{
|
||||
PLUGIN_COMPONENT_INSTANCE_WORLD, PLUGIN_COMPONENT_TOOL_WORLD, PLUGIN_RUNTIME_COMPONENT_KIND,
|
||||
PLUGIN_RUNTIME_WASM_KIND, PluginConfig, PluginDiscoveryLimits, PluginFsGrant,
|
||||
PluginFsOperation, PluginHostApi, PluginPermission, PluginRequestGrant, PluginSurface,
|
||||
PluginToolManifest, PluginWebSocketGrant, ResolvedPluginRecord,
|
||||
read_resolved_plugin_runtime_component,
|
||||
PluginConfig, PluginDiscoveryLimits, PluginFsGrant, PluginFsOperation, PluginHostApi,
|
||||
PluginPermission, PluginRequestGrant, PluginSurface, PluginToolManifest, PluginWebSocketGrant,
|
||||
ResolvedPluginRecord, read_resolved_plugin_runtime_component,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@@ -35,6 +34,8 @@ use tokio::runtime::{
|
||||
use tokio_tungstenite::tungstenite::client::IntoClientRequest;
|
||||
use tokio_tungstenite::tungstenite::protocol::{Message, WebSocketConfig};
|
||||
|
||||
const LEGACY_PLUGIN_RUNTIME_WASM_KIND: &str = "wasm";
|
||||
|
||||
use super::{
|
||||
FeatureDescriptor, FeatureId, FeatureInstallContext, FeatureInstallError, FeatureModule,
|
||||
FeatureRuntimeKind, ServiceDeclaration, ServiceId, ToolContribution, ToolDeclaration,
|
||||
@@ -235,12 +236,12 @@ pub fn inspect_resolved_plugin_static(record: &ResolvedPluginRecord) -> PluginSt
|
||||
diagnostic: None,
|
||||
}
|
||||
}
|
||||
Some(runtime) if runtime.kind == PLUGIN_RUNTIME_WASM_KIND => {
|
||||
Some(runtime) if runtime.kind == LEGACY_PLUGIN_RUNTIME_WASM_KIND => {
|
||||
let status = runtime
|
||||
.abi
|
||||
.as_deref()
|
||||
.map(|abi| format!("{PLUGIN_RUNTIME_WASM_KIND}/{abi}"))
|
||||
.unwrap_or_else(|| format!("{PLUGIN_RUNTIME_WASM_KIND}/<missing-abi>"));
|
||||
.map(|abi| format!("{LEGACY_PLUGIN_RUNTIME_WASM_KIND}/{abi}"))
|
||||
.unwrap_or_else(|| format!("{LEGACY_PLUGIN_RUNTIME_WASM_KIND}/<missing-abi>"));
|
||||
PluginRuntimeEligibility {
|
||||
eligible: false,
|
||||
status,
|
||||
@@ -3312,7 +3313,7 @@ impl PluginInstanceRuntime {
|
||||
PLUGIN_RUNTIME_COMPONENT_KIND => Err(PluginWasmError::Module(
|
||||
"unsupported or missing plugin component world".to_string(),
|
||||
)),
|
||||
PLUGIN_RUNTIME_WASM_KIND => Err(PluginWasmError::Module(
|
||||
LEGACY_PLUGIN_RUNTIME_WASM_KIND => Err(PluginWasmError::Module(
|
||||
"legacy raw wasm plugin runtime is not supported; use wasm-component".to_string(),
|
||||
)),
|
||||
other => Err(PluginWasmError::Module(format!(
|
||||
@@ -6036,7 +6037,7 @@ input_schema = {{ type = "object", additionalProperties = true }}
|
||||
fn legacy_raw_wasm_runtime_is_rejected_without_fallback_execution() {
|
||||
let mut record = record(vec![tool("PluginEcho")]);
|
||||
record.manifest.runtime = Some(PluginRuntimeManifest {
|
||||
kind: PLUGIN_RUNTIME_WASM_KIND.to_string(),
|
||||
kind: LEGACY_PLUGIN_RUNTIME_WASM_KIND.to_string(),
|
||||
entry: Some("plugin.wasm".to_string()),
|
||||
abi: Some("yoi-plugin-wasm-1".to_string()),
|
||||
component: None,
|
||||
|
||||
+108
-17
@@ -1853,6 +1853,39 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_raw_wasm_package_is_rejected_not_active_or_eligible() {
|
||||
let dir = tempdir().unwrap();
|
||||
let workspace = dir.path();
|
||||
fs::create_dir_all(workspace.join(".yoi/plugins")).unwrap();
|
||||
write_stored_zip(
|
||||
&workspace.join(".yoi/plugins/legacy.yoi-plugin"),
|
||||
&[
|
||||
("plugin.toml", plugin_legacy_manifest("legacy").as_bytes()),
|
||||
("plugin.wasm", b"not wasm"),
|
||||
],
|
||||
);
|
||||
|
||||
let snapshot = inspect_snapshot(workspace, &PluginConfig::default());
|
||||
let legacy = select_item(&snapshot, "project:legacy").unwrap();
|
||||
assert_eq!(legacy.status, "rejected");
|
||||
assert!(!legacy.discovered);
|
||||
assert!(!legacy.configured);
|
||||
assert!(legacy.enabled_surfaces.is_empty());
|
||||
assert!(legacy.diagnostics.iter().any(|diagnostic| {
|
||||
diagnostic.kind == "api"
|
||||
&& diagnostic.message.contains("legacy raw wasm")
|
||||
&& diagnostic.message.contains("wasm-component")
|
||||
}));
|
||||
|
||||
let list_output = render_list_snapshot_human(&snapshot).unwrap();
|
||||
assert!(list_output.contains("project:legacy [rejected]"));
|
||||
assert!(!list_output.contains("project:legacy [active]"));
|
||||
let show_output = render_item_human(legacy).unwrap();
|
||||
assert!(show_output.contains("status: rejected"));
|
||||
assert!(show_output.contains("legacy raw wasm"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_invalid_or_incompatible_package_is_rejected_not_missing() {
|
||||
let dir = tempdir().unwrap();
|
||||
@@ -1867,7 +1900,7 @@ mod tests {
|
||||
&workspace.join(".yoi/plugins/incompat.yoi-plugin"),
|
||||
&[
|
||||
("plugin.toml", incompatible_manifest.as_bytes()),
|
||||
("plugin.wasm", b"not wasm"),
|
||||
("plugin.component.wasm", b"not wasm"),
|
||||
],
|
||||
);
|
||||
let mut config = PluginConfig::default();
|
||||
@@ -1934,7 +1967,7 @@ mod tests {
|
||||
fs::create_dir_all(workspace.join(".yoi/plugins")).unwrap();
|
||||
write_stored_zip(
|
||||
&workspace.join(".yoi/plugins/no_manifest.yoi-plugin"),
|
||||
&[("plugin.wasm", b"not wasm")],
|
||||
&[("plugin.component.wasm", b"not wasm")],
|
||||
);
|
||||
let missing_runtime_manifest = plugin_manifest_missing_runtime_entry("missing_runtime");
|
||||
write_stored_zip(
|
||||
@@ -2140,7 +2173,7 @@ mod tests {
|
||||
plugin_manifest("echo", "echo", "object", &["echo"]),
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(plugin.join("plugin.wasm"), b"not wasm").unwrap();
|
||||
fs::write(plugin.join("plugin.component.wasm"), b"not wasm").unwrap();
|
||||
|
||||
let human = render_check(&plugin, &PluginCliArgs::default()).unwrap();
|
||||
assert!(human.contains("[active]"));
|
||||
@@ -2163,6 +2196,42 @@ mod tests {
|
||||
assert_eq!(value["safety"]["no_plugin_execution"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_check_rejects_legacy_raw_wasm_package() {
|
||||
let dir = tempdir().unwrap();
|
||||
let plugin = dir.path().join("legacy");
|
||||
fs::create_dir_all(&plugin).unwrap();
|
||||
fs::write(plugin.join("plugin.toml"), plugin_legacy_manifest("legacy")).unwrap();
|
||||
fs::write(plugin.join("plugin.wasm"), b"not wasm").unwrap();
|
||||
|
||||
let report = build_check_report(&plugin);
|
||||
assert_eq!(report.status, "rejected");
|
||||
assert!(report.diagnostics.iter().any(|diagnostic| {
|
||||
diagnostic.kind == "api"
|
||||
&& diagnostic.message.contains("legacy raw wasm")
|
||||
&& diagnostic.message.contains("wasm-component")
|
||||
}));
|
||||
let human = render_check_report(&report, &PluginCliArgs::default()).unwrap();
|
||||
assert!(human.contains("[rejected]"));
|
||||
assert!(human.contains("legacy raw wasm"));
|
||||
let json = render_check_report(
|
||||
&report,
|
||||
&PluginCliArgs {
|
||||
json: true,
|
||||
..PluginCliArgs::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(value["status"], "rejected");
|
||||
assert!(
|
||||
value["diagnostics"][0]["message"]
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.contains("wasm-component")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_check_rejects_invalid_manifest_and_missing_runtime_artifact() {
|
||||
let dir = tempdir().unwrap();
|
||||
@@ -2228,7 +2297,7 @@ mod tests {
|
||||
plugin_manifest("echo", "echo", "object", &["echo"]),
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(plugin.join("plugin.wasm"), b"not wasm").unwrap();
|
||||
fs::write(plugin.join("plugin.component.wasm"), b"not wasm").unwrap();
|
||||
let first = dir.path().join("first.yoi-plugin");
|
||||
let second = dir.path().join("second.yoi-plugin");
|
||||
|
||||
@@ -2466,9 +2535,9 @@ surfaces = ["tool"]
|
||||
permissions = [{{ kind = "surface", surface = "tool" }}, {{ kind = "tool", name = "Echo" }}]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "missing.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
kind = "wasm-component"
|
||||
component = "missing.component.wasm"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
|
||||
[[tools]]
|
||||
name = "Echo"
|
||||
@@ -2500,9 +2569,9 @@ surfaces = ["tool"]
|
||||
permissions = [{{ kind = "surface", surface = "tool" }}, {permissions}]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
|
||||
[[tools]]
|
||||
name = "{tool_name}"
|
||||
@@ -2512,6 +2581,28 @@ input_schema = {{ type = "{schema_type}" }}
|
||||
)
|
||||
}
|
||||
|
||||
fn plugin_legacy_manifest(id: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
schema_version = 1
|
||||
id = "{id}"
|
||||
name = "{id}"
|
||||
version = "0.1.0"
|
||||
surfaces = ["tool"]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
|
||||
[[tools]]
|
||||
name = "Echo"
|
||||
description = "Legacy raw wasm tool"
|
||||
input_schema = {{ type = "object" }}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
fn write_plugin_package(workspace: &Path, id: &str) -> String {
|
||||
let manifest = format!(
|
||||
r#"
|
||||
@@ -2523,9 +2614,9 @@ surfaces = ["tool"]
|
||||
permissions = [{{ kind = "surface", surface = "tool" }}, {{ kind = "tool", name = "Echo" }}]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
|
||||
[[tools]]
|
||||
name = "Echo"
|
||||
@@ -2547,9 +2638,9 @@ surfaces = ["tool"]
|
||||
permissions = [{{ kind = "surface", surface = "tool" }}, {{ kind = "tool", name = "Echo" }}, {{ kind = "tool", name = "Other" }}]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
|
||||
[[tools]]
|
||||
name = "Echo"
|
||||
@@ -2573,7 +2664,7 @@ input_schema = {{ type = "object" }}
|
||||
&package,
|
||||
&[
|
||||
("plugin.toml", manifest.as_bytes()),
|
||||
("plugin.wasm", b"not wasm"),
|
||||
("plugin.component.wasm", b"not wasm"),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user