feat: provide v2 Plugin Component contracts and offline validation
Validate exact Component interfaces and static Tool/DCDL schemas without guest execution. Share canonical archive packing and independent package/artifact verification; build real pure and HTTPS PDK fixtures and retire v1/Service authoring stubs.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "yoi-rust-component-https-template"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
# Keep the embedded template checkable in-place without making it a member of
|
||||
# Yoi's root workspace. A copied starter remains a normal standalone package.
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
yoi-plugin-pdk = { path = "../../../../crates/plugin-pdk" }
|
||||
|
||||
# Out-of-tree Plugin packages should replace the local path with a pinned
|
||||
# Yoi source commit. Use rev, not branch, for reproducible builds:
|
||||
# yoi-plugin-pdk = { git = "https://gitea.hareworks.net/Hare/yoi.git", package = "yoi-plugin-pdk", rev = "<pinned-yoi-commit-sha>" }
|
||||
@@ -0,0 +1,5 @@
|
||||
# Connection-bound HTTPS Tool雛形
|
||||
|
||||
接続先とsecretをguestが選べないよう、宣言slotからcall-local resourceを取得する。副作用の観測を失敗時にも保持し、自動再送やrollback成功を装わない。HTTP error statusはresponseとして扱う。
|
||||
|
||||
このソースをComponentへbuildできることは、HTTPS Hostの実装・認可・receipt保存を提供した証拠にはならない。packageの要求grantはWorkspace bindingや実行権限を自己発行しない。
|
||||
@@ -0,0 +1,28 @@
|
||||
schema_version = 2
|
||||
id = "example.rust_component_https"
|
||||
name = "Rust HTTPS Component Tool Template"
|
||||
version = "0.1.0"
|
||||
surfaces = ["tool"]
|
||||
config_schema = "{}"
|
||||
requested_grants = [{ kind = "https", slot = "api" }]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "yoi:plugin/https-tool-plugin@2.0.0"
|
||||
required_imports = ["yoi:host/errors@2.0.0", "yoi:host/connections@2.0.0", "yoi:host/http@2.0.0"]
|
||||
|
||||
[[tools]]
|
||||
name = "example_status"
|
||||
description = "Observe the status response through an explicitly bound HTTPS slot."
|
||||
input_schema = { type = "object", properties = {}, additionalProperties = false }
|
||||
output_schema = { type = "object", properties = { status = { type = "integer" }, body_bytes = { type = "integer" } }, required = ["status", "body_bytes"], additionalProperties = false }
|
||||
# Even GET may have side effects. Host receipt/admission policy still applies.
|
||||
error_codes = ["invalid_input", "invalid_output", "serialization", "unsupported_tool", "failed"]
|
||||
external_write = true
|
||||
|
||||
[[connections]]
|
||||
name = "api"
|
||||
# Package requests HTTPS bounds; Workspace binding supplies origin and secrets.
|
||||
methods = ["GET"]
|
||||
path_prefixes = ["/status"]
|
||||
@@ -0,0 +1,61 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yoi_plugin_pdk::wit_bindgen;
|
||||
use yoi_plugin_pdk::{ToolContext, ToolError, ToolOutput};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
world: "https-tool-plugin",
|
||||
path: "wit",
|
||||
generate_all,
|
||||
runtime_path: "yoi_plugin_pdk::wit_bindgen::rt",
|
||||
});
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Input {}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Output {
|
||||
status: u16,
|
||||
body_bytes: usize,
|
||||
}
|
||||
|
||||
fn handle_status(ctx: ToolContext, _input: Input) -> Result<ToolOutput, ToolError> {
|
||||
use yoi::host::{connections, http};
|
||||
if ctx.tool_name() != "example_status" {
|
||||
return Err(ToolError::unsupported_tool(ctx.tool_name()));
|
||||
}
|
||||
// This resource is call-local; the slot has no URL or credential value.
|
||||
let target = connections::open("api").map_err(host_failure)?;
|
||||
let response = http::send(
|
||||
&target,
|
||||
&http::Request {
|
||||
method: http::Method::Get,
|
||||
path: "/status".into(),
|
||||
query: Vec::new(),
|
||||
headers: Vec::new(),
|
||||
body: Vec::new(),
|
||||
timeout_ms: ctx.remaining_ms().min(15_000),
|
||||
// Stable within this logical invocation. Never automatically retry a failure.
|
||||
effect_key: "status-v1".into(),
|
||||
},
|
||||
)
|
||||
.map_err(host_failure)?;
|
||||
// HTTP 4xx/5xx remain responses, not transport failures. Do not log raw bodies.
|
||||
ToolOutput::json(
|
||||
"HTTP response observed",
|
||||
Output {
|
||||
status: response.status,
|
||||
body_bytes: response.body.len(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn host_failure(error: yoi::host::errors::HostError) -> ToolError {
|
||||
// Preserve the effect observation for the caller without claiming rollback or retry safety.
|
||||
ToolError::failed(format!(
|
||||
"Host {:?}; effect {:?}; diagnostic {}",
|
||||
error.code, error.effect, error.diagnostic_id
|
||||
))
|
||||
}
|
||||
|
||||
yoi_plugin_pdk::export_component_tool!(Plugin, handle_status);
|
||||
@@ -0,0 +1,61 @@
|
||||
package yoi:host@2.0.0;
|
||||
|
||||
interface errors {
|
||||
enum code {
|
||||
invalid-input, denied, not-found, conflict, unsupported,
|
||||
quota-exceeded, deadline-exceeded, cancelled, unavailable,
|
||||
indeterminate, receipt-expired,
|
||||
}
|
||||
enum effect-observation { not-dispatched, response-observed, indeterminate }
|
||||
record host-error {
|
||||
code: code,
|
||||
message: string,
|
||||
diagnostic-id: string,
|
||||
effect: effect-observation,
|
||||
}
|
||||
}
|
||||
|
||||
interface connections {
|
||||
use errors.{host-error};
|
||||
/// Call-local resource. No serialization, persistence or cross-Worker transfer.
|
||||
resource connection;
|
||||
open: func(slot: string) -> result<connection, host-error>;
|
||||
}
|
||||
|
||||
interface http {
|
||||
use connections.{connection};
|
||||
use errors.{host-error};
|
||||
enum method { get, head, post, put, patch, delete, options }
|
||||
record pair { name: string, value: string }
|
||||
record request {
|
||||
method: method,
|
||||
path: string,
|
||||
query: list<pair>,
|
||||
headers: list<pair>,
|
||||
body: list<u8>,
|
||||
timeout-ms: u32,
|
||||
effect-key: string,
|
||||
}
|
||||
record response {
|
||||
status: u16,
|
||||
headers: list<pair>,
|
||||
body: list<u8>,
|
||||
}
|
||||
/// Host authorizes each operation and does not automatically retry or follow redirects.
|
||||
send: func(target: borrow<connection>, input: request) -> result<response, host-error>;
|
||||
}
|
||||
|
||||
interface logging {
|
||||
use errors.{host-error};
|
||||
enum level { trace, debug, info, warn, error }
|
||||
variant scalar { text(string), boolean(bool), signed(s64), unsigned(u64), number(f64) }
|
||||
record field { name: string, value: scalar }
|
||||
record entry {
|
||||
level: level,
|
||||
event-code: string,
|
||||
message: string,
|
||||
fields: list<field>,
|
||||
}
|
||||
/// Bounded diagnostic data, not model context. Never include secrets or raw payloads.
|
||||
log: func(input: entry) -> result<_, host-error>;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package yoi:plugin@2.0.0;
|
||||
|
||||
/// Only plugin-specific payloads use schema-validated JSON. Context is not authority.
|
||||
interface tool {
|
||||
record invocation {
|
||||
invocation-id: string,
|
||||
tool-name: string,
|
||||
input-json: string,
|
||||
config-json: string,
|
||||
remaining-ms: u32,
|
||||
}
|
||||
record output {
|
||||
summary: string,
|
||||
content: option<string>,
|
||||
structured-json: option<string>,
|
||||
}
|
||||
record tool-error {
|
||||
code: string,
|
||||
message: string,
|
||||
}
|
||||
invoke: func(input: invocation) -> result<output, tool-error>;
|
||||
}
|
||||
|
||||
/// Pure computation: no ambient WASI or unused Host capabilities.
|
||||
world tool-plugin {
|
||||
export tool;
|
||||
}
|
||||
|
||||
world https-tool-plugin {
|
||||
import yoi:host/errors@2.0.0;
|
||||
import yoi:host/connections@2.0.0;
|
||||
import yoi:host/http@2.0.0;
|
||||
export tool;
|
||||
}
|
||||
|
||||
/// Logging is an explicit capability, never an ambient console.
|
||||
world logged-tool-plugin {
|
||||
import yoi:host/errors@2.0.0;
|
||||
import yoi:host/logging@2.0.0;
|
||||
export tool;
|
||||
}
|
||||
|
||||
world https-logged-tool-plugin {
|
||||
import yoi:host/errors@2.0.0;
|
||||
import yoi:host/connections@2.0.0;
|
||||
import yoi:host/http@2.0.0;
|
||||
import yoi:host/logging@2.0.0;
|
||||
export tool;
|
||||
}
|
||||
Reference in New Issue
Block a user