merge: plugin component model runtime
This commit is contained in:
commit
63d7ad788d
648
Cargo.lock
generated
648
Cargo.lock
generated
|
|
@ -2,6 +2,15 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
|
|
@ -82,6 +91,12 @@ version = "1.0.102"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.9.1"
|
||||
|
|
@ -222,6 +237,9 @@ name = "bumpalo"
|
|||
version = "3.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
|
|
@ -351,6 +369,15 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
|
|
@ -422,6 +449,15 @@ version = "0.8.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpp_demangle"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
|
|
@ -440,6 +476,157 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-assembler-x64"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bc293b86236abcc45f2f72e2d18e2bd636f2a08b75eb286bae31e71e1430c91"
|
||||
dependencies = [
|
||||
"cranelift-assembler-x64-meta",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-assembler-x64-meta"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b954c826eddaf1b001402cb8aecf1764c6f6d637ba69fb9e3311f1ebac965be6"
|
||||
dependencies = [
|
||||
"cranelift-srcgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-bforest"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4053fa2575ef4a5c35d2708533df2200400ae979226cea9cc92a578b811bd4e7"
|
||||
dependencies = [
|
||||
"cranelift-entity",
|
||||
"wasmtime-internal-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-bitset"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d216663191014aa63e1d2cffd058e609eaf207646d40b739d88250f65b2c4f69"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"wasmtime-internal-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a5e7e7aad6a425a51da1ad7ab9e5d280ea97eb7c7c4545fafb567915a75aadb"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"cranelift-assembler-x64",
|
||||
"cranelift-bforest",
|
||||
"cranelift-bitset",
|
||||
"cranelift-codegen-meta",
|
||||
"cranelift-codegen-shared",
|
||||
"cranelift-control",
|
||||
"cranelift-entity",
|
||||
"cranelift-isle",
|
||||
"gimli",
|
||||
"hashbrown 0.17.1",
|
||||
"libm",
|
||||
"log",
|
||||
"pulley-interpreter",
|
||||
"regalloc2",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
"wasmtime-internal-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-meta"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c421d80a9a85f806cb02a2983b5b5368a335c319795b1f1b4b771a24479af5b0"
|
||||
dependencies = [
|
||||
"cranelift-assembler-x64-meta",
|
||||
"cranelift-codegen-shared",
|
||||
"cranelift-srcgen",
|
||||
"heck",
|
||||
"pulley-interpreter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-shared"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78fdb83ab012d0ee6a44ced7ca8788a444f17cf821c62f95d6ef87c9f0262518"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-control"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b75adc6eb7bb4ac6365106afb6cac4f12fe1ddfa02ddc9fd7015ca1469b471b"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-entity"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "668e56db75a54816cbdd7c7b7bfc558b08bf7b2cda9d0846491517e92f3b393b"
|
||||
dependencies = [
|
||||
"cranelift-bitset",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"wasmtime-internal-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-frontend"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c63892dc1cc3ae48680183fa66997f60ffe7f1e200c8d390f8ee66edff4aef5a"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-isle"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94eaf429c32a12715429c7c6ddfdd43c170f4cdd7e97bfa507bd68a652091087"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-native"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd77674904ae9be11c1e1efdba54788b59f3d6658d747b97534bfbba2909aacc"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"libc",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-srcgen"
|
||||
version = "0.132.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cba7c0ff5941842c36653da155580ce41e675c204a67ac1b4e1c478a9347bbb7"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
|
|
@ -700,6 +887,18 @@ version = "1.15.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
|
|
@ -1027,6 +1226,18 @@ dependencies = [
|
|||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"hashbrown 0.16.1",
|
||||
"indexmap",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
|
|
@ -1122,6 +1333,17 @@ dependencies = [
|
|||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
dependencies = [
|
||||
"foldhash 0.2.0",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
|
|
@ -1463,12 +1685,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.1"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"hashbrown 0.17.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
|
@ -1793,6 +2015,15 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "manifest"
|
||||
version = "0.1.0"
|
||||
|
|
@ -1852,6 +2083,15 @@ version = "2.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "memfd"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227"
|
||||
dependencies = [
|
||||
"rustix 1.1.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.10"
|
||||
|
|
@ -2077,6 +2317,18 @@ version = "0.2.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.39.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
|
|
@ -2368,6 +2620,7 @@ dependencies = [
|
|||
"tracing",
|
||||
"uuid",
|
||||
"wasmi",
|
||||
"wasmtime",
|
||||
"wat",
|
||||
"workflow",
|
||||
]
|
||||
|
|
@ -2403,6 +2656,18 @@ version = "1.13.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.5"
|
||||
|
|
@ -2499,6 +2764,29 @@ dependencies = [
|
|||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulley-interpreter"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d9880c1985ccccaed3646b0ef793dc39a4b117403ed4afc6fa3ef6027c5200f"
|
||||
dependencies = [
|
||||
"cranelift-bitset",
|
||||
"log",
|
||||
"pulley-macros",
|
||||
"wasmtime-internal-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulley-macros"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee249346855ad102580e474da5463f86f8a7d449e6d49e00fefb304e448e2983"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.9"
|
||||
|
|
@ -2770,6 +3058,20 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regalloc2"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de2c52737737f8609e94f975dee22854a2d5c125772d4b1cf292120f4d45c186"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
"hashbrown 0.17.1",
|
||||
"log",
|
||||
"rustc-hash",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
|
|
@ -2860,6 +3162,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.2"
|
||||
|
|
@ -3091,6 +3399,10 @@ name = "semver"
|
|||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
|
|
@ -3341,6 +3653,9 @@ name = "smallvec"
|
|||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
|
|
@ -3501,6 +3816,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
||||
|
||||
[[package]]
|
||||
name = "target-triple"
|
||||
version = "1.0.0"
|
||||
|
|
@ -4260,12 +4581,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.246.2"
|
||||
version = "0.248.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61fb705ce81adde29d2a8e99d87995e39a6e927358c91398f374474746070ef7"
|
||||
checksum = "ac92cf547bc18d27ecc521015c08c353b4f18b84ab388bb6d1b6b682c620d9b6"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser 0.246.2",
|
||||
"wasmparser 0.248.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.252.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser 0.252.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4357,9 +4688,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.246.2"
|
||||
version = "0.248.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71cde4757396defafd25417cfb36aa3161027d06d865b0c24baaae229aac005d"
|
||||
checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
"semver",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.252.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"indexmap",
|
||||
|
|
@ -4367,23 +4711,257 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "246.0.2"
|
||||
name = "wasmprinter"
|
||||
version = "0.248.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe3fe8e3bf88ad96d031b4181ddbd64634b17cb0d06dfc3de589ef43591a9a62"
|
||||
checksum = "30b264a5410b008d4d199a92bf536eae703cbd614482fc1ec53831cf19e1c183"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"termcolor",
|
||||
"wasmparser 0.248.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7ce9aa2c67f75fadcfdc6aa9097d03e7c39485dfe316f2ed6a7c0fd186c527"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"async-trait",
|
||||
"bitflags 2.11.0",
|
||||
"bumpalo",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"encoding_rs",
|
||||
"libc",
|
||||
"log",
|
||||
"mach2",
|
||||
"memfd",
|
||||
"object",
|
||||
"once_cell",
|
||||
"postcard",
|
||||
"pulley-interpreter",
|
||||
"rustix 1.1.4",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
"wasmparser 0.248.0",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-internal-component-macro",
|
||||
"wasmtime-internal-component-util",
|
||||
"wasmtime-internal-core",
|
||||
"wasmtime-internal-cranelift",
|
||||
"wasmtime-internal-fiber",
|
||||
"wasmtime-internal-jit-debug",
|
||||
"wasmtime-internal-jit-icache-coherence",
|
||||
"wasmtime-internal-unwinder",
|
||||
"wasmtime-internal-versioned-export-macros",
|
||||
"wasmtime-internal-winch",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-environ"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8fb157bd1fbf689ac89d570433a700db6f33bdfcb5ffc30e3f1c49e4c70de71"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cpp_demangle",
|
||||
"cranelift-bforest",
|
||||
"cranelift-bitset",
|
||||
"cranelift-entity",
|
||||
"gimli",
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
"log",
|
||||
"object",
|
||||
"postcard",
|
||||
"rustc-demangle",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2 0.10.9",
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
"wasm-encoder 0.248.0",
|
||||
"wasmparser 0.248.0",
|
||||
"wasmprinter",
|
||||
"wasmtime-internal-component-util",
|
||||
"wasmtime-internal-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-component-macro"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b96c17f35fae2ab574667aba0c58fd56349a6f788ac42541a2e543116d5cfb91"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
"wasmtime-internal-component-util",
|
||||
"wasmtime-internal-wit-bindgen",
|
||||
"wit-parser 0.248.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-component-util"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d2eeb9b53222859e6f5dc73d2ccfb33254d672469cac11b693a71912e2f3817"
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-core"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1deaf6bc3430abd7497b00c64f06ca2b97ca0fe41af87836446ca30949965c"
|
||||
dependencies = [
|
||||
"hashbrown 0.17.1",
|
||||
"libm",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-cranelift"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b845f83b5b04b11bc48329b53eb4fa8cf9f28a43c71ed8e1203f68ffa9806d1b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cranelift-codegen",
|
||||
"cranelift-control",
|
||||
"cranelift-entity",
|
||||
"cranelift-frontend",
|
||||
"cranelift-native",
|
||||
"gimli",
|
||||
"itertools",
|
||||
"log",
|
||||
"object",
|
||||
"pulley-interpreter",
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
"thiserror 2.0.18",
|
||||
"wasmparser 0.248.0",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-internal-core",
|
||||
"wasmtime-internal-unwinder",
|
||||
"wasmtime-internal-versioned-export-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-fiber"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10c8466f72965ae85c250f90aaa7992c089a2f8502009bd0d2c9e7d6409174a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rustix 1.1.4",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-internal-versioned-export-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-jit-debug"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d3adfecf5621b14d8f8871f4cb4ed9f844197b1ddefc702ef4c859552cd9551"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"wasmtime-internal-versioned-export-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-jit-icache-coherence"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d3c1e9fb618ec45c9b3477ea683cd37bee427273d7b13bba5c66a1caaf1dd6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasmtime-internal-core",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-unwinder"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7aa91132b81f1e172ec7e7c3c114ac34209ee6b3524b3a8d6943af99803f66c5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
"object",
|
||||
"wasmtime-environ",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-versioned-export-macros"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea811ffe23f597cc7708327ea25d9eb018dcf760ffe15ccb7d0b27ad635de61"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-winch"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "828b66175c54a0d00b4c1c1c76658d8aa73aeb9fa3553575c5eee56d40f2eb18"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"gimli",
|
||||
"log",
|
||||
"object",
|
||||
"target-lexicon",
|
||||
"wasmparser 0.248.0",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-internal-cranelift",
|
||||
"winch-codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-internal-wit-bindgen"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae00896ad9bef1b3ca6401ae9a841daa6f357dd91541b6baf87082946d1bde1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.11.0",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"wit-parser 0.248.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "252.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "942a3449d6a593fccc111a6241c8df52bda168af30e40bf9580d4394d7374c65"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"leb128fmt",
|
||||
"memchr",
|
||||
"unicode-width",
|
||||
"wasm-encoder 0.246.2",
|
||||
"wasm-encoder 0.252.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.246.2"
|
||||
version = "1.252.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bd7fda1199b94fff395c2d19a153f05dbe7807630316fa9673367666fd2ad8c"
|
||||
checksum = "c72a4ba7088f7bac94cf516e49882bdf97068904a563768cf249efc839ec42cb"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
|
@ -4529,6 +5107,25 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "winch-codegen"
|
||||
version = "45.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89c09acfdfa281b3340e1e94ef3cf6618d69eab975280f881e154c29f49419c1"
|
||||
dependencies = [
|
||||
"cranelift-assembler-x64",
|
||||
"cranelift-codegen",
|
||||
"gimli",
|
||||
"regalloc2",
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
"thiserror 2.0.18",
|
||||
"wasmparser 0.248.0",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-internal-core",
|
||||
"wasmtime-internal-cranelift",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
|
|
@ -4802,7 +5399,7 @@ checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
"wit-parser 0.244.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4852,7 +5449,7 @@ dependencies = [
|
|||
"wasm-encoder 0.244.0",
|
||||
"wasm-metadata",
|
||||
"wasmparser 0.244.0",
|
||||
"wit-parser",
|
||||
"wit-parser 0.244.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4873,6 +5470,25 @@ dependencies = [
|
|||
"wasmparser 0.244.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.248.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "247ad505da2915a082fe13204c5ba8788425aea1de54f43b284818cf82637856"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hashbrown 0.17.1",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser 0.248.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "workflow"
|
||||
version = "0.1.0"
|
||||
|
|
|
|||
|
|
@ -378,12 +378,28 @@ 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.
|
||||
///
|
||||
/// 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"`.
|
||||
pub const PLUGIN_RUNTIME_COMPONENT_KIND: &str = "wasm-component";
|
||||
pub const PLUGIN_COMPONENT_TOOL_WORLD: &str = "yoi:plugin/tool@1.0.0";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PluginRuntimeManifest {
|
||||
pub kind: String,
|
||||
pub entry: String,
|
||||
#[serde(default)]
|
||||
pub entry: Option<String>,
|
||||
#[serde(default)]
|
||||
pub abi: Option<String>,
|
||||
#[serde(default)]
|
||||
pub component: Option<String>,
|
||||
#[serde(default)]
|
||||
pub world: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
@ -837,7 +853,7 @@ pub fn read_resolved_plugin_runtime_module(
|
|||
.with_digest(&record.digest)
|
||||
})?;
|
||||
|
||||
if runtime.kind != "wasm" {
|
||||
if runtime.kind != PLUGIN_RUNTIME_WASM_KIND {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
|
|
@ -848,7 +864,7 @@ pub fn read_resolved_plugin_runtime_module(
|
|||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest));
|
||||
}
|
||||
if runtime.abi.as_deref() != Some("yoi-plugin-wasm-1") {
|
||||
if runtime.abi.as_deref() != Some(PLUGIN_RUNTIME_WASM_ABI) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
|
|
@ -860,6 +876,18 @@ pub fn read_resolved_plugin_runtime_module(
|
|||
.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,
|
||||
|
|
@ -926,13 +954,13 @@ pub fn read_resolved_plugin_runtime_module(
|
|||
}
|
||||
|
||||
validate_manifest_path(
|
||||
&runtime.entry,
|
||||
entry,
|
||||
&archive,
|
||||
&record.package_label,
|
||||
record.source,
|
||||
&record.manifest.id,
|
||||
)?;
|
||||
let normalized = normalize_archive_path(&runtime.entry).ok_or_else(|| {
|
||||
let normalized = normalize_archive_path(entry).ok_or_else(|| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Traversal,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
|
|
@ -956,6 +984,154 @@ pub fn read_resolved_plugin_runtime_module(
|
|||
})
|
||||
}
|
||||
|
||||
/// 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(
|
||||
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 component runtime",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})?;
|
||||
|
||||
if runtime.kind != PLUGIN_RUNTIME_COMPONENT_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.world.as_deref() != Some(PLUGIN_COMPONENT_TOOL_WORLD) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin component world is unsupported",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest));
|
||||
}
|
||||
let component = runtime.component.as_deref().ok_or_else(|| {
|
||||
PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Missing,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin component runtime artifact 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(
|
||||
component,
|
||||
&archive,
|
||||
&record.package_label,
|
||||
record.source,
|
||||
&record.manifest.id,
|
||||
)?;
|
||||
let normalized = normalize_archive_path(component).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 component artifact is missing from the package",
|
||||
)
|
||||
.with_source(record.source)
|
||||
.with_identity(&record.identity)
|
||||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct PluginStore {
|
||||
source: PluginSourceKind,
|
||||
|
|
@ -1237,27 +1413,84 @@ fn validate_manifest(
|
|||
.with_package(label));
|
||||
}
|
||||
if let Some(runtime) = &manifest.runtime {
|
||||
if runtime.kind != "wasm" {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin runtime kind is unsupported",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
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)?;
|
||||
}
|
||||
PLUGIN_RUNTIME_COMPONENT_KIND => {
|
||||
if runtime.abi.is_some() || runtime.entry.is_some() {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Malformed,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin component runtime must not declare raw WASM ABI metadata",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
if runtime.world.as_deref() != Some(PLUGIN_COMPONENT_TOOL_WORLD) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin component world is unsupported",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
let Some(component) = runtime.component.as_deref() else {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Missing,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin component runtime artifact is required",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
};
|
||||
validate_manifest_path(component, archive, label, source, &manifest.id)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin runtime kind is unsupported",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
}
|
||||
if runtime.abi.as_deref() != Some("yoi-plugin-wasm-1") {
|
||||
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));
|
||||
}
|
||||
validate_manifest_path(&runtime.entry, archive, label, source, &manifest.id)?;
|
||||
}
|
||||
for hook in &manifest.hooks {
|
||||
if !is_safe_id(&hook.id) {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ uuid = { workspace = true, features = ["v7"] }
|
|||
session-metrics = { workspace = true }
|
||||
arc-swap = "1.9.1"
|
||||
wasmi = { version = "0.51.1", default-features = false, features = ["std", "extra-checks"] }
|
||||
wasmtime = { version = "45.0.2", default-features = false, features = ["std", "runtime", "cranelift", "component-model"] }
|
||||
|
||||
[dev-dependencies]
|
||||
dotenv = "0.15.0"
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ use llm_worker::tool::{
|
|||
Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOrigin, ToolOutput,
|
||||
};
|
||||
use manifest::plugin::{
|
||||
PluginConfig, PluginDiscoveryLimits, PluginFsGrant, PluginFsOperation, PluginHostApi,
|
||||
PluginPermission, PluginSurface, PluginToolManifest, ResolvedPluginRecord,
|
||||
PLUGIN_COMPONENT_TOOL_WORLD, PLUGIN_RUNTIME_COMPONENT_KIND, PLUGIN_RUNTIME_WASM_ABI,
|
||||
PLUGIN_RUNTIME_WASM_KIND, PluginConfig, PluginDiscoveryLimits, PluginFsGrant,
|
||||
PluginFsOperation, PluginHostApi, PluginPermission, PluginSurface, PluginToolManifest,
|
||||
ResolvedPluginRecord, read_resolved_plugin_runtime_component,
|
||||
read_resolved_plugin_runtime_module,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -134,26 +136,51 @@ pub struct PluginToolEligibility {
|
|||
pub fn inspect_resolved_plugin_static(record: &ResolvedPluginRecord) -> PluginStaticInspection {
|
||||
let runtime = match &record.manifest.runtime {
|
||||
Some(runtime)
|
||||
if runtime.kind == "wasm" && runtime.abi.as_deref() == Some("yoi-plugin-wasm-1") =>
|
||||
if runtime.kind == PLUGIN_RUNTIME_WASM_KIND
|
||||
&& runtime.abi.as_deref() == Some(PLUGIN_RUNTIME_WASM_ABI)
|
||||
&& runtime.entry.is_some() =>
|
||||
{
|
||||
PluginRuntimeEligibility {
|
||||
eligible: true,
|
||||
status: "wasm/yoi-plugin-wasm-1".to_string(),
|
||||
status: format!("{PLUGIN_RUNTIME_WASM_KIND}/{PLUGIN_RUNTIME_WASM_ABI}"),
|
||||
diagnostic: None,
|
||||
}
|
||||
}
|
||||
Some(runtime) if runtime.kind == "wasm" => {
|
||||
Some(runtime) if runtime.kind == PLUGIN_RUNTIME_WASM_KIND => {
|
||||
let status = runtime
|
||||
.abi
|
||||
.as_deref()
|
||||
.map(|abi| format!("wasm/{abi}"))
|
||||
.unwrap_or_else(|| "wasm/<missing-abi>".to_string());
|
||||
.map(|abi| format!("{PLUGIN_RUNTIME_WASM_KIND}/{abi}"))
|
||||
.unwrap_or_else(|| format!("{PLUGIN_RUNTIME_WASM_KIND}/<missing-abi>"));
|
||||
PluginRuntimeEligibility {
|
||||
eligible: false,
|
||||
status,
|
||||
diagnostic: Some("unsupported or missing plugin runtime ABI".to_string()),
|
||||
}
|
||||
}
|
||||
Some(runtime)
|
||||
if runtime.kind == PLUGIN_RUNTIME_COMPONENT_KIND
|
||||
&& runtime.world.as_deref() == Some(PLUGIN_COMPONENT_TOOL_WORLD)
|
||||
&& runtime.component.is_some() =>
|
||||
{
|
||||
PluginRuntimeEligibility {
|
||||
eligible: true,
|
||||
status: format!("{PLUGIN_RUNTIME_COMPONENT_KIND}/{PLUGIN_COMPONENT_TOOL_WORLD}"),
|
||||
diagnostic: None,
|
||||
}
|
||||
}
|
||||
Some(runtime) if runtime.kind == PLUGIN_RUNTIME_COMPONENT_KIND => {
|
||||
let status = runtime
|
||||
.world
|
||||
.as_deref()
|
||||
.map(|world| format!("{PLUGIN_RUNTIME_COMPONENT_KIND}/{world}"))
|
||||
.unwrap_or_else(|| format!("{PLUGIN_RUNTIME_COMPONENT_KIND}/<missing-world>"));
|
||||
PluginRuntimeEligibility {
|
||||
eligible: false,
|
||||
status,
|
||||
diagnostic: Some("unsupported or missing plugin component world".to_string()),
|
||||
}
|
||||
}
|
||||
Some(runtime) => PluginRuntimeEligibility {
|
||||
eligible: false,
|
||||
status: runtime.kind.clone(),
|
||||
|
|
@ -1484,6 +1511,17 @@ const PLUGIN_FS_MAX_READ_BYTES: usize = 64 * 1024;
|
|||
const PLUGIN_FS_MAX_WRITE_BYTES: usize = 64 * 1024;
|
||||
const PLUGIN_FS_MAX_LIST_ENTRIES: usize = 256;
|
||||
|
||||
fn wasm_component_store_limits() -> wasmtime::StoreLimits {
|
||||
wasmtime::StoreLimitsBuilder::new()
|
||||
.memory_size(PLUGIN_WASM_MEMORY_BYTES)
|
||||
.table_elements(PLUGIN_WASM_TABLE_ELEMENTS)
|
||||
.instances(1)
|
||||
.tables(1)
|
||||
.memories(1)
|
||||
.trap_on_grow_failure(true)
|
||||
.build()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum PluginFsRuntimeOperation {
|
||||
Read,
|
||||
|
|
@ -1708,26 +1746,25 @@ impl Tool for PluginWasmTool {
|
|||
let plugin_ref = self.origin.plugin_ref.clone();
|
||||
let digest = self.origin.digest.clone();
|
||||
let input = input_json.as_bytes().to_vec();
|
||||
let execution =
|
||||
tokio::task::spawn_blocking(move || run_plugin_wasm_tool(record, name, input));
|
||||
let execution = tokio::task::spawn_blocking(move || run_plugin_tool(record, name, input));
|
||||
match tokio::time::timeout(PLUGIN_WASM_TIMEOUT, execution).await {
|
||||
Ok(Ok(Ok(output))) => Ok(output),
|
||||
Ok(Ok(Err(error))) => Err(ToolError::ExecutionFailed(format!(
|
||||
"plugin WASM tool `{}` from `{}` (digest {}) failed closed: {}",
|
||||
"plugin tool `{}` from `{}` (digest {}) failed closed: {}",
|
||||
self.name,
|
||||
plugin_ref,
|
||||
digest,
|
||||
error.bounded_message()
|
||||
))),
|
||||
Ok(Err(error)) => Err(ToolError::ExecutionFailed(format!(
|
||||
"plugin WASM tool `{}` from `{}` (digest {}) cancelled/failed to join: {}",
|
||||
"plugin tool `{}` from `{}` (digest {}) cancelled/failed to join: {}",
|
||||
self.name,
|
||||
plugin_ref,
|
||||
digest,
|
||||
bounded_message(error.to_string())
|
||||
))),
|
||||
Err(_) => Err(ToolError::ExecutionFailed(format!(
|
||||
"plugin WASM tool `{}` from `{}` (digest {}) timed out after {:?}",
|
||||
"plugin tool `{}` from `{}` (digest {}) timed out after {:?}",
|
||||
self.name, plugin_ref, digest, PLUGIN_WASM_TIMEOUT
|
||||
))),
|
||||
}
|
||||
|
|
@ -1767,6 +1804,28 @@ struct PluginWasmHostState {
|
|||
store_limits: wasmi::StoreLimits,
|
||||
}
|
||||
|
||||
fn run_plugin_tool(
|
||||
record: ResolvedPluginRecord,
|
||||
tool_name: String,
|
||||
input: Vec<u8>,
|
||||
) -> Result<ToolOutput, PluginWasmError> {
|
||||
match record
|
||||
.manifest
|
||||
.runtime
|
||||
.as_ref()
|
||||
.map(|runtime| runtime.kind.as_str())
|
||||
{
|
||||
Some(PLUGIN_RUNTIME_WASM_KIND) => run_plugin_wasm_tool(record, tool_name, input),
|
||||
Some(PLUGIN_RUNTIME_COMPONENT_KIND) => run_plugin_component_tool(record, tool_name, input),
|
||||
Some(other) => Err(PluginWasmError::Module(format!(
|
||||
"unsupported plugin runtime kind `{other}`"
|
||||
))),
|
||||
None => Err(PluginWasmError::Package(
|
||||
"plugin runtime is not declared".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_plugin_wasm_tool(
|
||||
record: ResolvedPluginRecord,
|
||||
tool_name: String,
|
||||
|
|
@ -1864,6 +1923,225 @@ fn run_plugin_wasm_tool_with_https_client(
|
|||
decode_plugin_wasm_output(&store.data().output)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct PluginComponentHostState {
|
||||
record: ResolvedPluginRecord,
|
||||
https_client: Arc<dyn PluginHttpsClient>,
|
||||
store_limits: wasmtime::StoreLimits,
|
||||
}
|
||||
|
||||
fn run_plugin_component_tool(
|
||||
record: ResolvedPluginRecord,
|
||||
tool_name: String,
|
||||
input: Vec<u8>,
|
||||
) -> Result<ToolOutput, PluginWasmError> {
|
||||
run_plugin_component_tool_with_https_client(
|
||||
record,
|
||||
tool_name,
|
||||
input,
|
||||
Arc::new(ReqwestPluginHttpsClient),
|
||||
)
|
||||
}
|
||||
|
||||
fn run_plugin_component_tool_with_https_client(
|
||||
record: ResolvedPluginRecord,
|
||||
tool_name: String,
|
||||
input: Vec<u8>,
|
||||
https_client: Arc<dyn PluginHttpsClient>,
|
||||
) -> Result<ToolOutput, PluginWasmError> {
|
||||
let tool = record
|
||||
.manifest
|
||||
.tools
|
||||
.iter()
|
||||
.find(|tool| tool.name == tool_name)
|
||||
.ok_or_else(|| {
|
||||
PluginWasmError::Module("requested tool is not declared by plugin manifest".to_string())
|
||||
})?;
|
||||
authorize_plugin_tool(&record, tool).map_err(|error| {
|
||||
PluginWasmError::Module(format!(
|
||||
"plugin permission denied: {}",
|
||||
error.bounded_message()
|
||||
))
|
||||
})?;
|
||||
|
||||
let limits = PluginDiscoveryLimits::default();
|
||||
let component_bytes = read_resolved_plugin_runtime_component(&record, &limits)
|
||||
.map_err(|diagnostic| PluginWasmError::Package(diagnostic.message))?;
|
||||
if component_bytes.len() > limits.max_file_size_bytes as usize {
|
||||
return Err(PluginWasmError::Package(format!(
|
||||
"WASM component runtime artifact exceeds {} bytes",
|
||||
limits.max_file_size_bytes
|
||||
)));
|
||||
}
|
||||
|
||||
let mut config = wasmtime::Config::new();
|
||||
config.wasm_component_model(true);
|
||||
config.consume_fuel(true);
|
||||
config.max_wasm_stack(8 * 1024 * 1024);
|
||||
let engine = wasmtime::Engine::new(&config)
|
||||
.map_err(|error| PluginWasmError::Module(error.to_string()))?;
|
||||
let component =
|
||||
wasmtime::component::Component::new(&engine, &component_bytes).map_err(|error| {
|
||||
PluginWasmError::Module(format!("component is incompatible: {error:?}"))
|
||||
})?;
|
||||
validate_component_imports(&record, &engine, &component)?;
|
||||
|
||||
let mut linker = wasmtime::component::Linker::<PluginComponentHostState>::new(&engine);
|
||||
define_plugin_component_host_imports(&mut linker)?;
|
||||
let mut store = wasmtime::Store::new(
|
||||
&engine,
|
||||
PluginComponentHostState {
|
||||
record: record.clone(),
|
||||
https_client,
|
||||
store_limits: wasm_component_store_limits(),
|
||||
},
|
||||
);
|
||||
store.limiter(|state| &mut state.store_limits);
|
||||
store
|
||||
.set_fuel(PLUGIN_WASM_FUEL)
|
||||
.map_err(|error| PluginWasmError::Execution(error.to_string()))?;
|
||||
let instance = linker
|
||||
.instantiate(&mut store, &component)
|
||||
.map_err(|error| PluginWasmError::Execution(error.to_string()))?;
|
||||
let call = instance
|
||||
.get_typed_func::<(&str, &str), (String,)>(&mut store, "call")
|
||||
.map_err(|error| {
|
||||
PluginWasmError::Module(format!(
|
||||
"component does not export expected `{}` call function: {error}",
|
||||
PLUGIN_COMPONENT_TOOL_WORLD
|
||||
))
|
||||
})?;
|
||||
let input_json = std::str::from_utf8(&input).map_err(|error| {
|
||||
PluginWasmError::Output(format!("plugin component input is not UTF-8: {error}"))
|
||||
})?;
|
||||
// Wasmtime lifts the returned WIT `string` into a host `String` before the
|
||||
// ordinary ToolOutput JSON cap can be applied. Keep the component store on
|
||||
// the same memory/table/instance limits as the raw WASM runtime so an
|
||||
// untrusted component can only force host string allocation from bounded
|
||||
// component memory; oversized memories/tables/instances fail closed during
|
||||
// instantiation/growth before this lift succeeds.
|
||||
let (output,) = call
|
||||
.call(&mut store, (&tool_name, input_json))
|
||||
.map_err(|error| PluginWasmError::Execution(error.to_string()))?;
|
||||
decode_plugin_wasm_output(output.as_bytes())
|
||||
}
|
||||
|
||||
fn validate_component_imports(
|
||||
record: &ResolvedPluginRecord,
|
||||
engine: &wasmtime::Engine,
|
||||
component: &wasmtime::component::Component,
|
||||
) -> Result<(), PluginWasmError> {
|
||||
for (name, _) in component.component_type().imports(engine) {
|
||||
match name {
|
||||
"yoi:host/https@1.0.0" => {
|
||||
authorize_plugin_host_api(record, PluginHostApi::Https).map_err(|error| {
|
||||
PluginWasmError::Module(format!(
|
||||
"plugin host API dispatch denied: {}",
|
||||
error.bounded_message()
|
||||
))
|
||||
})?;
|
||||
}
|
||||
"yoi:host/fs@1.0.0" => {
|
||||
authorize_plugin_host_api(record, PluginHostApi::Fs).map_err(|error| {
|
||||
PluginWasmError::Module(format!(
|
||||
"plugin host API dispatch denied: {}",
|
||||
error.bounded_message()
|
||||
))
|
||||
})?;
|
||||
}
|
||||
other => {
|
||||
return Err(PluginWasmError::Module(format!(
|
||||
"unsupported component import `{other}`; no WASI filesystem, ambient network, environment, or other imports are available"
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn define_plugin_component_host_imports(
|
||||
linker: &mut wasmtime::component::Linker<PluginComponentHostState>,
|
||||
) -> Result<(), PluginWasmError> {
|
||||
linker
|
||||
.root()
|
||||
.instance("yoi:host/https@1.0.0")
|
||||
.map_err(|error| PluginWasmError::Module(error.to_string()))?
|
||||
.func_wrap(
|
||||
"request",
|
||||
|store: wasmtime::StoreContextMut<'_, PluginComponentHostState>,
|
||||
(request,): (String,)|
|
||||
-> wasmtime::Result<(String,)> {
|
||||
authorize_plugin_host_api(&store.data().record, PluginHostApi::Https)
|
||||
.map_err(|error| wasmtime::Error::msg(error.bounded_message()))?;
|
||||
let response = execute_plugin_https_request(
|
||||
&store.data().record,
|
||||
store.data().https_client.as_ref(),
|
||||
request.as_bytes(),
|
||||
)
|
||||
.map_err(|error| wasmtime::Error::msg(error.0))?;
|
||||
Ok((String::from_utf8_lossy(&response).into_owned(),))
|
||||
},
|
||||
)
|
||||
.map_err(|error| PluginWasmError::Module(error.to_string()))?;
|
||||
|
||||
let mut root = linker.root();
|
||||
let mut fs = root
|
||||
.instance("yoi:host/fs@1.0.0")
|
||||
.map_err(|error| PluginWasmError::Module(error.to_string()))?;
|
||||
fs.func_wrap(
|
||||
"read",
|
||||
|store: wasmtime::StoreContextMut<'_, PluginComponentHostState>,
|
||||
(request,): (String,)|
|
||||
-> wasmtime::Result<(String,)> {
|
||||
authorize_plugin_host_api(&store.data().record, PluginHostApi::Fs)
|
||||
.map_err(|error| wasmtime::Error::msg(error.bounded_message()))?;
|
||||
execute_plugin_fs_request(
|
||||
&store.data().record,
|
||||
PluginFsRuntimeOperation::Read,
|
||||
request.as_bytes(),
|
||||
)
|
||||
.map(|bytes| (String::from_utf8_lossy(&bytes).into_owned(),))
|
||||
.map_err(|error| wasmtime::Error::msg(error.message))
|
||||
},
|
||||
)
|
||||
.map_err(|error| PluginWasmError::Module(error.to_string()))?;
|
||||
fs.func_wrap(
|
||||
"list",
|
||||
|store: wasmtime::StoreContextMut<'_, PluginComponentHostState>,
|
||||
(request,): (String,)|
|
||||
-> wasmtime::Result<(String,)> {
|
||||
authorize_plugin_host_api(&store.data().record, PluginHostApi::Fs)
|
||||
.map_err(|error| wasmtime::Error::msg(error.bounded_message()))?;
|
||||
execute_plugin_fs_request(
|
||||
&store.data().record,
|
||||
PluginFsRuntimeOperation::List,
|
||||
request.as_bytes(),
|
||||
)
|
||||
.map(|bytes| (String::from_utf8_lossy(&bytes).into_owned(),))
|
||||
.map_err(|error| wasmtime::Error::msg(error.message))
|
||||
},
|
||||
)
|
||||
.map_err(|error| PluginWasmError::Module(error.to_string()))?;
|
||||
fs.func_wrap(
|
||||
"write",
|
||||
|store: wasmtime::StoreContextMut<'_, PluginComponentHostState>,
|
||||
(request,): (String,)|
|
||||
-> wasmtime::Result<(String,)> {
|
||||
authorize_plugin_host_api(&store.data().record, PluginHostApi::Fs)
|
||||
.map_err(|error| wasmtime::Error::msg(error.bounded_message()))?;
|
||||
execute_plugin_fs_request(
|
||||
&store.data().record,
|
||||
PluginFsRuntimeOperation::Write,
|
||||
request.as_bytes(),
|
||||
)
|
||||
.map(|bytes| (String::from_utf8_lossy(&bytes).into_owned(),))
|
||||
.map_err(|error| wasmtime::Error::msg(error.message))
|
||||
},
|
||||
)
|
||||
.map_err(|error| PluginWasmError::Module(error.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_wasm_imports(
|
||||
record: &ResolvedPluginRecord,
|
||||
module: &wasmi::Module,
|
||||
|
|
@ -3679,9 +3957,11 @@ mod tests {
|
|||
fn record_with_missing_package_runtime() -> ResolvedPluginRecord {
|
||||
let mut record = record(vec![tool("PluginSearch")]);
|
||||
record.manifest.runtime = Some(PluginRuntimeManifest {
|
||||
kind: "wasm".into(),
|
||||
entry: "plugin.wasm".into(),
|
||||
abi: Some("yoi-plugin-wasm-1".into()),
|
||||
kind: PLUGIN_RUNTIME_WASM_KIND.into(),
|
||||
entry: Some("plugin.wasm".into()),
|
||||
abi: Some(PLUGIN_RUNTIME_WASM_ABI.into()),
|
||||
component: None,
|
||||
world: None,
|
||||
});
|
||||
record
|
||||
}
|
||||
|
|
@ -3736,6 +4016,347 @@ mod tests {
|
|||
(dir, record)
|
||||
}
|
||||
|
||||
fn write_component_plugin_package(path: &Path, component: &[u8], world: &str) {
|
||||
let manifest = format!(
|
||||
r#"schema_version = 1
|
||||
id = "example"
|
||||
name = "Example"
|
||||
version = "1.0.0"
|
||||
description = "Example component plugin"
|
||||
surfaces = ["tool"]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "{}"
|
||||
|
||||
[[permissions]]
|
||||
kind = "surface"
|
||||
surface = "tool"
|
||||
|
||||
[[permissions]]
|
||||
kind = "tool"
|
||||
name = "PluginEcho"
|
||||
|
||||
[[tools]]
|
||||
name = "PluginEcho"
|
||||
description = "Echo plugin tool"
|
||||
input_schema = {{ type = "object", additionalProperties = true }}
|
||||
"#,
|
||||
world
|
||||
);
|
||||
write_stored_zip(
|
||||
path,
|
||||
&[
|
||||
("plugin.toml", manifest.as_bytes()),
|
||||
("plugin.component.wasm", component),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn resolved_record_with_component(component: Vec<u8>) -> (TempDir, ResolvedPluginRecord) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let package_dir = dir.path().join(".yoi/plugins");
|
||||
fs::create_dir_all(&package_dir).unwrap();
|
||||
let package_path = package_dir.join("component.yoi-plugin");
|
||||
write_component_plugin_package(&package_path, &component, PLUGIN_COMPONENT_TOOL_WORLD);
|
||||
let config = PluginConfig {
|
||||
enabled: vec![PluginEnablementConfig {
|
||||
id: "project:example".parse().unwrap(),
|
||||
surfaces: vec![PluginSurface::Tool],
|
||||
..PluginEnablementConfig::default()
|
||||
}],
|
||||
resolved: Vec::new(),
|
||||
diagnostics: Vec::new(),
|
||||
};
|
||||
let options = PluginDiscoveryOptions::new(dir.path());
|
||||
let resolved = resolve_plugin_config_for_startup(&config, &options);
|
||||
assert!(
|
||||
resolved.diagnostics.is_empty(),
|
||||
"{:#?}",
|
||||
resolved.diagnostics
|
||||
);
|
||||
assert_eq!(resolved.resolved.len(), 1);
|
||||
let mut record = resolved.resolved[0].clone();
|
||||
record.grants = PluginGrantConfig {
|
||||
id: Some(record.identity.to_string()),
|
||||
version: Some(PluginExactVersion(record.version.clone())),
|
||||
digest: Some(record.digest.clone()),
|
||||
permissions: tool_permissions(&record.manifest.tools),
|
||||
https: Vec::new(),
|
||||
fs: Vec::new(),
|
||||
};
|
||||
(dir, record)
|
||||
}
|
||||
|
||||
fn component_tool_that_returns(output: &[u8]) -> Vec<u8> {
|
||||
component_tool_with_memory_pages(output, 1)
|
||||
}
|
||||
|
||||
fn component_tool_with_memory_pages(output: &[u8], memory_pages: usize) -> Vec<u8> {
|
||||
wat::parse_str(format!(
|
||||
r#"(component
|
||||
(core module $m
|
||||
(memory (export "memory") {})
|
||||
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
|
||||
(if (result i32) (i32.eqz (local.get 0))
|
||||
(then (i32.const 8192))
|
||||
(else (local.get 0))))
|
||||
(data (i32.const 1024) "{}")
|
||||
(func (export "call") (param i32 i32 i32 i32) (result i32)
|
||||
(i32.store (i32.const 2048) (i32.const 1024))
|
||||
(i32.store (i32.const 2052) (i32.const {}))
|
||||
(i32.const 2048))
|
||||
)
|
||||
(core instance $i (instantiate $m))
|
||||
(alias core export $i "memory" (core memory $mem))
|
||||
(alias core export $i "realloc" (core func $realloc))
|
||||
(alias core export $i "call" (core func $call_core))
|
||||
(type $call_ty (func (param "tool-name" string) (param "input-json" string) (result string)))
|
||||
(func $call (type $call_ty) (canon lift (core func $call_core) (memory $mem) (realloc $realloc) string-encoding=utf8))
|
||||
(export "call" (func $call))
|
||||
)"#,
|
||||
memory_pages,
|
||||
wat_bytes(output),
|
||||
output.len()
|
||||
))
|
||||
.expect("valid component wat")
|
||||
}
|
||||
|
||||
fn component_tool_with_table_elements(output: &[u8], table_elements: usize) -> Vec<u8> {
|
||||
wat::parse_str(format!(
|
||||
r#"(component
|
||||
(core module $m
|
||||
(memory (export "memory") 1)
|
||||
(table {} funcref)
|
||||
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
|
||||
(if (result i32) (i32.eqz (local.get 0))
|
||||
(then (i32.const 8192))
|
||||
(else (local.get 0))))
|
||||
(data (i32.const 1024) "{}")
|
||||
(func (export "call") (param i32 i32 i32 i32) (result i32)
|
||||
(i32.store (i32.const 2048) (i32.const 1024))
|
||||
(i32.store (i32.const 2052) (i32.const {}))
|
||||
(i32.const 2048))
|
||||
)
|
||||
(core instance $i (instantiate $m))
|
||||
(alias core export $i "memory" (core memory $mem))
|
||||
(alias core export $i "realloc" (core func $realloc))
|
||||
(alias core export $i "call" (core func $call_core))
|
||||
(type $call_ty (func (param "tool-name" string) (param "input-json" string) (result string)))
|
||||
(func $call (type $call_ty) (canon lift (core func $call_core) (memory $mem) (realloc $realloc) string-encoding=utf8))
|
||||
(export "call" (func $call))
|
||||
)"#,
|
||||
table_elements,
|
||||
wat_bytes(output),
|
||||
output.len()
|
||||
))
|
||||
.expect("valid component wat")
|
||||
}
|
||||
|
||||
fn component_tool_importing_https(output: &[u8]) -> Vec<u8> {
|
||||
wat::parse_str(format!(
|
||||
r#"(component
|
||||
(import "yoi:host/https@1.0.0" (instance $https (export "request" (func $request (param "request-json" string) (result string)))))
|
||||
(core module $m
|
||||
(memory (export "memory") 1)
|
||||
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
|
||||
(if (result i32) (i32.eqz (local.get 0))
|
||||
(then (i32.const 8192))
|
||||
(else (local.get 0))))
|
||||
(data (i32.const 1024) "{}")
|
||||
(func (export "call") (param i32 i32 i32 i32) (result i32)
|
||||
(i32.store (i32.const 2048) (i32.const 1024))
|
||||
(i32.store (i32.const 2052) (i32.const {}))
|
||||
(i32.const 2048))
|
||||
)
|
||||
(core instance $i (instantiate $m))
|
||||
(alias core export $i "memory" (core memory $mem))
|
||||
(alias core export $i "realloc" (core func $realloc))
|
||||
(alias core export $i "call" (core func $call_core))
|
||||
(type $call_ty (func (param "tool-name" string) (param "input-json" string) (result string)))
|
||||
(func $call (type $call_ty) (canon lift (core func $call_core) (memory $mem) (realloc $realloc) string-encoding=utf8))
|
||||
(export "call" (func $call))
|
||||
)"#,
|
||||
wat_bytes(output),
|
||||
output.len()
|
||||
))
|
||||
.expect("valid component wat")
|
||||
}
|
||||
|
||||
fn component_without_call_export() -> Vec<u8> {
|
||||
wat::parse_str(r#"(component (core module $m) (core instance $i (instantiate $m)))"#)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn raw_module_bytes() -> Vec<u8> {
|
||||
wat::parse_str(r#"(module (func (export "call")))"#).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_tool_executes_through_ordinary_tool_result_path() {
|
||||
let (_dir, record) = resolved_record_with_component(component_tool_that_returns(
|
||||
br#"{"summary":"component ok","content":"ordinary tool result path"}"#,
|
||||
));
|
||||
|
||||
let output = run_plugin_component_tool(record, "PluginEcho".to_string(), b"{}".to_vec())
|
||||
.expect("component tool output");
|
||||
|
||||
assert_eq!(output.summary, "component ok");
|
||||
assert_eq!(output.content.as_deref(), Some("ordinary tool result path"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_memory_limit_fails_closed_before_string_lift() {
|
||||
let oversized_memory_pages = (PLUGIN_WASM_MEMORY_BYTES / 65_536) + 1;
|
||||
let (_dir, record) = resolved_record_with_component(component_tool_with_memory_pages(
|
||||
br#"{"summary":"should not lift"}"#,
|
||||
oversized_memory_pages,
|
||||
));
|
||||
|
||||
let error = run_plugin_component_tool(record, "PluginEcho".to_string(), b"{}".to_vec())
|
||||
.expect_err("component memory limit is enforced");
|
||||
|
||||
assert!(format!("{error:?}").contains("growing memory"), "{error:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_table_limit_fails_closed() {
|
||||
let (_dir, record) = resolved_record_with_component(component_tool_with_table_elements(
|
||||
br#"{"summary":"should not run"}"#,
|
||||
PLUGIN_WASM_TABLE_ELEMENTS + 1,
|
||||
));
|
||||
|
||||
let error = run_plugin_component_tool(record, "PluginEcho".to_string(), b"{}".to_vec())
|
||||
.expect_err("component table limit is enforced");
|
||||
|
||||
assert!(format!("{error:?}").contains("growing table"), "{error:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_output_cap_still_fails_closed_after_bounded_lift() {
|
||||
let output = format!(
|
||||
r#"{{"summary":"too big","content":"{}"}}"#,
|
||||
"x".repeat(PLUGIN_WASM_MAX_OUTPUT_BYTES)
|
||||
);
|
||||
let (_dir, record) =
|
||||
resolved_record_with_component(component_tool_with_memory_pages(output.as_bytes(), 2));
|
||||
|
||||
let error = run_plugin_component_tool(record, "PluginEcho".to_string(), b"{}".to_vec())
|
||||
.expect_err("component output cap is enforced");
|
||||
|
||||
assert!(format!("{error:?}").contains("output exceeds"), "{error:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_tool_denies_host_import_without_matching_grant() {
|
||||
let (_dir, record) = resolved_record_with_component(component_tool_importing_https(
|
||||
br#"{"summary":"component ok"}"#,
|
||||
));
|
||||
|
||||
let error = run_plugin_component_tool(record, "PluginEcho".to_string(), b"{}".to_vec())
|
||||
.expect_err("host import without grant is denied");
|
||||
|
||||
assert!(
|
||||
format!("{error:?}").contains("plugin host API dispatch denied"),
|
||||
"{error:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_tool_missing_export_fails_closed() {
|
||||
let (_dir, record) = resolved_record_with_component(component_without_call_export());
|
||||
|
||||
let error = run_plugin_component_tool(record, "PluginEcho".to_string(), b"{}".to_vec())
|
||||
.expect_err("missing export fails closed");
|
||||
|
||||
assert!(
|
||||
format!("{error:?}").contains("does not export expected"),
|
||||
"{error:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn core_wasm_is_not_silently_reinterpreted_as_component() {
|
||||
let (_dir, record) = resolved_record_with_component(raw_module_bytes());
|
||||
|
||||
let error = run_plugin_component_tool(record, "PluginEcho".to_string(), b"{}".to_vec())
|
||||
.expect_err("core module is incompatible with component runtime");
|
||||
|
||||
assert!(
|
||||
format!("{error:?}").contains("component is incompatible"),
|
||||
"{error:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_wrong_world_fails_closed_during_discovery() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let package_dir = dir.path().join(".yoi/plugins");
|
||||
fs::create_dir_all(&package_dir).unwrap();
|
||||
let package_path = package_dir.join("component.yoi-plugin");
|
||||
write_component_plugin_package(
|
||||
&package_path,
|
||||
&component_tool_that_returns(br#"{"summary":"component ok"}"#),
|
||||
"example:other/world@1.0.0",
|
||||
);
|
||||
let config = PluginConfig {
|
||||
enabled: vec![PluginEnablementConfig {
|
||||
id: "project:example".parse().unwrap(),
|
||||
surfaces: vec![PluginSurface::Tool],
|
||||
..PluginEnablementConfig::default()
|
||||
}],
|
||||
resolved: Vec::new(),
|
||||
diagnostics: Vec::new(),
|
||||
};
|
||||
let options = PluginDiscoveryOptions::new(dir.path());
|
||||
let resolved = resolve_plugin_config_for_startup(&config, &options);
|
||||
|
||||
assert!(resolved.resolved.is_empty());
|
||||
assert!(
|
||||
resolved.diagnostics.iter().any(|diagnostic| diagnostic
|
||||
.message
|
||||
.contains("component world is unsupported")),
|
||||
"{:#?}",
|
||||
resolved.diagnostics
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_tool_registration_uses_existing_tool_registry_path() {
|
||||
let (_dir, record) = resolved_record_with_component(component_tool_that_returns(
|
||||
br#"{"summary":"component ok"}"#,
|
||||
));
|
||||
let (report, pending) = install_plugin_record(record);
|
||||
|
||||
assert_eq!(skipped_count(&report), 0, "{report:#?}");
|
||||
assert_eq!(pending.len(), 1);
|
||||
let (meta, _) = pending[0]();
|
||||
assert_eq!(meta.name, "PluginEcho");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_static_inspection_reports_component_runtime_without_execution() {
|
||||
let mut record = record(vec![tool("Echo")]);
|
||||
record.package_path = std::path::PathBuf::from("/no/such/component.wasm");
|
||||
record.manifest.runtime = Some(PluginRuntimeManifest {
|
||||
kind: PLUGIN_RUNTIME_COMPONENT_KIND.to_string(),
|
||||
entry: None,
|
||||
abi: None,
|
||||
component: Some("plugin.component.wasm".to_string()),
|
||||
world: Some(PLUGIN_COMPONENT_TOOL_WORLD.to_string()),
|
||||
});
|
||||
|
||||
let inspection = inspect_resolved_plugin_static(&record);
|
||||
|
||||
assert!(inspection.runtime.eligible);
|
||||
assert_eq!(
|
||||
inspection.runtime.status,
|
||||
format!("{PLUGIN_RUNTIME_COMPONENT_KIND}/{PLUGIN_COMPONENT_TOOL_WORLD}")
|
||||
);
|
||||
assert!(inspection.runtime.diagnostic.is_none());
|
||||
}
|
||||
|
||||
fn write_plugin_package(path: &Path, wasm: &[u8]) {
|
||||
let manifest = br#"schema_version = 1
|
||||
id = "example"
|
||||
|
|
@ -3884,9 +4505,11 @@ input_schema = { type = "object", additionalProperties = true }
|
|||
let mut record = record(vec![tool("Echo")]);
|
||||
record.package_path = std::path::PathBuf::from("/no/such/plugin.wasm");
|
||||
record.manifest.runtime = Some(PluginRuntimeManifest {
|
||||
kind: "wasm".to_string(),
|
||||
entry: "plugin.wasm".to_string(),
|
||||
abi: Some("yoi-plugin-wasm-1".to_string()),
|
||||
kind: PLUGIN_RUNTIME_WASM_KIND.to_string(),
|
||||
entry: Some("plugin.wasm".to_string()),
|
||||
abi: Some(PLUGIN_RUNTIME_WASM_ABI.to_string()),
|
||||
component: None,
|
||||
world: None,
|
||||
});
|
||||
|
||||
let inspection = inspect_resolved_plugin_static(&record);
|
||||
|
|
@ -3901,9 +4524,11 @@ input_schema = { type = "object", additionalProperties = true }
|
|||
fn static_inspection_reports_missing_tool_grant() {
|
||||
let mut record = record(vec![tool("Echo")]);
|
||||
record.manifest.runtime = Some(PluginRuntimeManifest {
|
||||
kind: "wasm".to_string(),
|
||||
entry: "plugin.wasm".to_string(),
|
||||
abi: Some("yoi-plugin-wasm-1".to_string()),
|
||||
kind: PLUGIN_RUNTIME_WASM_KIND.to_string(),
|
||||
entry: Some("plugin.wasm".to_string()),
|
||||
abi: Some(PLUGIN_RUNTIME_WASM_ABI.to_string()),
|
||||
component: None,
|
||||
world: None,
|
||||
});
|
||||
record.grants.permissions = vec![PluginPermission::surface(PluginSurface::Tool)];
|
||||
|
||||
|
|
@ -3926,9 +4551,11 @@ input_schema = { type = "object", additionalProperties = true }
|
|||
bad_schema.input_schema = json!({"type":"string"});
|
||||
let mut record = record(vec![bad_schema]);
|
||||
record.manifest.runtime = Some(PluginRuntimeManifest {
|
||||
kind: "wasm".to_string(),
|
||||
entry: "plugin.wasm".to_string(),
|
||||
abi: Some("yoi-plugin-wasm-1".to_string()),
|
||||
kind: PLUGIN_RUNTIME_WASM_KIND.to_string(),
|
||||
entry: Some("plugin.wasm".to_string()),
|
||||
abi: Some(PLUGIN_RUNTIME_WASM_ABI.to_string()),
|
||||
component: None,
|
||||
world: None,
|
||||
});
|
||||
|
||||
let inspection = inspect_resolved_plugin_static(&record);
|
||||
|
|
@ -3953,9 +4580,11 @@ input_schema = { type = "object", additionalProperties = true }
|
|||
second_duplicate.input_schema = json!({"type":"object"});
|
||||
let mut record = record(vec![invalid, first_duplicate, second_duplicate]);
|
||||
record.manifest.runtime = Some(PluginRuntimeManifest {
|
||||
kind: "wasm".to_string(),
|
||||
entry: "plugin.wasm".to_string(),
|
||||
abi: Some("yoi-plugin-wasm-1".to_string()),
|
||||
kind: PLUGIN_RUNTIME_WASM_KIND.to_string(),
|
||||
entry: Some("plugin.wasm".to_string()),
|
||||
abi: Some(PLUGIN_RUNTIME_WASM_ABI.to_string()),
|
||||
component: None,
|
||||
world: None,
|
||||
});
|
||||
|
||||
let inspection = inspect_resolved_plugin_static(&record);
|
||||
|
|
|
|||
|
|
@ -121,3 +121,57 @@ For example, `https` should be modeled as typed request/response data with expli
|
|||
- It does not replace Plugin grants with WIT imports.
|
||||
- It does not introduce Service, Ingress, WebSocket, or inbound HTTP by itself.
|
||||
- It does not merge Plugin and MCP. MCP remains a separate untrusted tool/resource/prompt bridge with its own policy.
|
||||
|
||||
## Implemented runtime boundary
|
||||
|
||||
Plugin Tool packages now select the runtime explicitly in `plugin.toml`:
|
||||
|
||||
```toml
|
||||
[runtime]
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
```
|
||||
|
||||
The legacy core-Wasm ABI remains explicit and is not reinterpreted as a
|
||||
component:
|
||||
|
||||
```toml
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
```
|
||||
|
||||
The component runtime uses `wasmtime::component` and expects the exported world
|
||||
`yoi:plugin/tool@1.0.0` with a `call(tool-name: string, input-json: string) ->
|
||||
string` export. The returned string is the same ToolOutput JSON used by the raw
|
||||
runtime, so registration and execution still flow through the existing
|
||||
ToolRegistry and Worker Tool-result history path.
|
||||
|
||||
Host imports are stable names under `yoi:host/*@1.0.0`; the repository WIT files
|
||||
live in `resources/plugin/wit/`. Importing `yoi:host/https@1.0.0` or
|
||||
`yoi:host/fs@1.0.0` is not authority. The runtime checks package grants before
|
||||
component instantiation and checks again on every host call. No WASI filesystem,
|
||||
network, environment, or other ambient imports are linked.
|
||||
|
||||
Static discovery and `yoi plugin list/show` only parse package manifests and
|
||||
reported runtime metadata. They do not instantiate or execute the component.
|
||||
Wrong `world`, missing artifact metadata, missing `call` export, unsupported
|
||||
imports, or core-Wasm bytes in a component package all fail closed with bounded
|
||||
Plugin diagnostics or ordinary Tool errors.
|
||||
|
||||
See `docs/examples/plugin-component-tool/lib.rs` for a minimal
|
||||
`wit-bindgen`/SDK-style authoring sketch. Package authors should generate
|
||||
bindings from `resources/plugin/wit`, build a component artifact, and set the
|
||||
component runtime metadata above.
|
||||
|
||||
### v1 request/response shape
|
||||
|
||||
The v1 component world intentionally keeps Tool input, Tool output, and host API
|
||||
payloads as JSON strings. This is a migration bridge that preserves the existing
|
||||
ToolOutput schema, Tool history behavior, grant checks, and raw-Wasm host API
|
||||
semantics while moving package authors onto WIT/canonical ABI bindings.
|
||||
Structured WIT records for Tool requests/responses/errors and host HTTPS/FS
|
||||
payloads are deferred to a follow-up API-design step rather than accidentally
|
||||
omitted.
|
||||
|
|
|
|||
|
|
@ -204,3 +204,29 @@ Good follow-up Tickets are intentionally separable:
|
|||
6. WASM package ABI, initialization limits, host-function grants, and Tool/Hook contribution plumbing.
|
||||
7. Optional lock-file or pin update workflow for reproducible fresh startup.
|
||||
8. Future MCP/plugin bridge, only if explicitly approved as a separate design and implementation effort.
|
||||
|
||||
### Component Model Tool runtime
|
||||
|
||||
Tool packages may use WebAssembly Component Model runtime metadata:
|
||||
|
||||
```toml
|
||||
[runtime]
|
||||
kind = "wasm-component"
|
||||
component = "plugin.component.wasm"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
```
|
||||
|
||||
This is separate from the legacy raw core-Wasm runtime:
|
||||
|
||||
```toml
|
||||
[runtime]
|
||||
kind = "wasm"
|
||||
entry = "plugin.wasm"
|
||||
abi = "yoi-plugin-wasm-1"
|
||||
```
|
||||
|
||||
Component packages must not use `entry`/`abi`; raw packages must not use
|
||||
`component`/`world`. Discovery reports the selected runtime kind/world without
|
||||
executing the artifact. Component execution still requires explicit package
|
||||
enablement, exact source/version/digest grants, and matching Tool/host API
|
||||
permissions.
|
||||
|
|
|
|||
23
docs/examples/plugin-component-tool/lib.rs
Normal file
23
docs/examples/plugin-component-tool/lib.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//! Minimal Component Model Tool plugin authoring sketch.
|
||||
//!
|
||||
//! Build this as a `wasm32-unknown-unknown` cdylib with `wit-bindgen`-generated
|
||||
//! exports and package the adapted component as `plugin.component.wasm`.
|
||||
|
||||
wit_bindgen::generate!({
|
||||
world: "tool",
|
||||
path: "../../../resources/plugin/wit",
|
||||
});
|
||||
|
||||
struct Plugin;
|
||||
|
||||
impl Guest for Plugin {
|
||||
fn call(tool_name: String, input_json: String) -> String {
|
||||
// Ordinary ToolOutput JSON. The runtime routes this through the normal
|
||||
// Worker/Tool result path; no context is injected by the component.
|
||||
format!(
|
||||
r#"{{"summary":"component tool {tool_name}","content":"input was {input_json}"}}"#
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export!(Plugin);
|
||||
|
|
@ -40,7 +40,7 @@ rustPlatform.buildRustPackage rec {
|
|||
filter = sourceFilter;
|
||||
};
|
||||
|
||||
cargoHash = "sha256-xqax43t9IevkNG2lZvfRP562ORKb3aHxUNsQwS1FK/k=";
|
||||
cargoHash = "sha256-i4U7wXPoWIHA4EAJZva2HQXNN8P5+RhGVGNBAOZVGk0=";
|
||||
|
||||
depsExtraArgs = {
|
||||
# Older fetchCargoVendor utilities used crates.io's API download endpoint,
|
||||
|
|
|
|||
15
resources/plugin/wit/yoi-host-v1.wit
Normal file
15
resources/plugin/wit/yoi-host-v1.wit
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package yoi:host@1.0.0;
|
||||
|
||||
/// Grant-bound HTTPS host API. Importing this interface does not grant
|
||||
/// authority; package grants are checked before registration/execution and on
|
||||
/// every host call.
|
||||
interface https {
|
||||
request: func(request-json: string) -> string;
|
||||
}
|
||||
|
||||
/// Grant-bound filesystem host API. No ambient WASI filesystem is exposed.
|
||||
interface fs {
|
||||
read: func(request-json: string) -> string;
|
||||
list: func(request-json: string) -> string;
|
||||
write: func(request-json: string) -> string;
|
||||
}
|
||||
11
resources/plugin/wit/yoi-plugin-tool-v1.wit
Normal file
11
resources/plugin/wit/yoi-plugin-tool-v1.wit
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package yoi:plugin@1.0.0;
|
||||
|
||||
world tool {
|
||||
import yoi:host/https@1.0.0;
|
||||
import yoi:host/fs@1.0.0;
|
||||
|
||||
/// Execute a manifest-declared Tool. `input-json` is the normal Tool input
|
||||
/// JSON and the returned string is the same ToolOutput JSON accepted by the
|
||||
/// legacy raw-Wasm ABI.
|
||||
export call: func(tool-name: string, input-json: string) -> string;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user