diff --git a/Cargo.lock b/Cargo.lock index ffc3735b..7aa6ad02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,22 +37,60 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-consensus" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "serde", + "sha2", +] + +[[package]] +name = "alloy-eips" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "once_cell", + "serde", +] + +[[package]] +name = "alloy-genesis" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4#6f8ebb45afca1a201a11d421ec46db0f7a1d8d08" +source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" dependencies = [ "alloy-primitives", "serde", "serde_json", "thiserror", + "tracing", ] [[package]] name = "alloy-primitives" -version = "0.6.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" dependencies = [ "alloy-rlp", "bytes", @@ -95,24 +133,98 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4#6f8ebb45afca1a201a11d421ec46db0f7a1d8d08" +source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", "alloy-primitives", "alloy-rlp", + "alloy-serde", + "alloy-sol-types", "itertools 0.12.1", "serde", "serde_json", "thiserror", ] +[[package]] +name = "alloy-serde" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck 0.5.0", + "indexmap", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" +dependencies = [ + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.66", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-types" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4#6f8ebb45afca1a201a11d421ec46db0f7a1d8d08" +source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" dependencies = [ "alloy-json-rpc", - "base64", + "base64 0.22.1", "futures-util", + "futures-utils-wasm", "serde", "serde_json", "thiserror", @@ -169,9 +281,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -366,6 +478,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -429,6 +547,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blst" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -477,11 +607,25 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c-kzg" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf100c4cea8f207e883ff91ca886d621d8a166cb04971dfaa9bb8fd99ed95df" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "serde", +] + [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", @@ -518,18 +662,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -539,9 +683,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "color-eyre" @@ -578,9 +722,9 @@ checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "const-hex" -version = "1.11.4" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -775,6 +919,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "ecdsa" version = "0.16.9" @@ -996,6 +1157,12 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "generic-array" version = "0.14.7" @@ -1039,6 +1206,12 @@ dependencies = [ "url", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "group" version = "0.13.0" @@ -1084,6 +1257,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1149,9 +1328,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2" [[package]] name = "httpdate" @@ -1161,9 +1340,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -1219,6 +1398,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "id-arena" version = "2.2.1" @@ -1227,12 +1524,14 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1283,9 +1582,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -1355,7 +1654,6 @@ dependencies = [ "elliptic-curve", "once_cell", "sha2", - "signature", ] [[package]] @@ -1370,8 +1668,8 @@ dependencies = [ [[package]] name = "kinode_process_lib" -version = "0.6.0" -source = "git+https://github.com/kinode-dao/process_lib.git?rev=84b3d84#84b3d84c7c31185f15691a288f1b45dbffb18fe2" +version = "0.8.0" +source = "git+https://github.com/kinode-dao/process_lib.git?rev=7eb3a04#7eb3a04f9be79d1cc3a52fa460faeea7ba3008ed" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -1382,6 +1680,7 @@ dependencies = [ "http 1.1.0", "mime_guess", "rand", + "rmp-serde", "serde", "serde_json", "thiserror", @@ -1391,10 +1690,10 @@ dependencies = [ [[package]] name = "kit" -version = "0.5.3" +version = "0.6.0" dependencies = [ "anyhow", - "base64", + "base64 0.21.7", "clap", "color-eyre", "dirs", @@ -1422,6 +1721,7 @@ dependencies = [ "tracing-error", "tracing-subscriber", "walkdir", + "wit-bindgen", "zip", ] @@ -1505,6 +1805,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -1574,11 +1880,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -1910,11 +2215,35 @@ dependencies = [ "toml_edit 0.21.1", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -1959,7 +2288,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "rusty-fork", "tempfile", "unarray", @@ -2047,14 +2376,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2068,13 +2397,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -2085,9 +2414,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" @@ -2095,7 +2424,7 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -2173,9 +2502,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -2197,9 +2526,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" @@ -2250,7 +2579,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", ] [[package]] @@ -2358,18 +2687,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -2495,9 +2824,9 @@ dependencies = [ [[package]] name = "spdx" -version = "0.10.4" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9" +checksum = "47317bbaf63785b53861e1ae2d11b80d6b624211d42cb20efcd210ee6f8a14bc" dependencies = [ "smallvec", ] @@ -2524,6 +2853,12 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2564,12 +2899,35 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2639,6 +2997,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "time" version = "0.3.36" @@ -2680,25 +3047,20 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -2714,9 +3076,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", @@ -2735,9 +3097,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" dependencies = [ "futures-util", "log", @@ -2760,14 +3122,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.14", ] [[package]] @@ -2792,15 +3154,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.13", ] [[package]] @@ -2935,9 +3297,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" dependencies = [ "byteorder", "bytes", @@ -2948,7 +3310,6 @@ dependencies = [ "rand", "sha1", "thiserror", - "url", "utf-8", ] @@ -2991,27 +3352,12 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -3026,9 +3372,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -3041,11 +3387,23 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -3401,9 +3759,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -3454,7 +3812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30acbe8fb708c3a830a33c4cb705df82659bf831b492ec6ca1a17a369cfeeafb" dependencies = [ "anyhow", - "heck", + "heck 0.4.1", "indexmap", "wasm-metadata", "wit-bindgen-core", @@ -3512,6 +3870,18 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -3521,11 +3891,56 @@ dependencies = [ "tap", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -3541,6 +3956,28 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 07573671..6e6bebd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kit" -version = "0.5.3" +version = "0.6.0" edition = "2021" [build-dependencies] @@ -15,7 +15,7 @@ dirs = "5.0" fs-err = "2.11" futures-util = "0.3" hex = "0.4" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib.git", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib.git", rev = "7eb3a04" } nix = { version = "0.27", features = ["process", "signal", "term"] } procfs = "0.16" regex = "1" @@ -43,6 +43,7 @@ tracing-appender = "0.2" tracing-error = "0.2" tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "std"] } walkdir = "2.4" +wit-bindgen = "0.24.0" zip = "0.6" [[bin]] diff --git a/src/boot_fake_node/mod.rs b/src/boot_fake_node/mod.rs index d6713c9d..5336d0f9 100644 --- a/src/boot_fake_node/mod.rs +++ b/src/boot_fake_node/mod.rs @@ -40,7 +40,7 @@ struct Asset { } #[instrument(level = "trace", skip_all)] -fn extract_zip(archive_path: &Path) -> Result<()> { +pub fn extract_zip(archive_path: &Path) -> Result<()> { let file = fs::File::open(archive_path)?; let mut archive = ZipArchive::new(file)?; diff --git a/src/build/mod.rs b/src/build/mod.rs index 16d61334..3ad1547e 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -1,24 +1,33 @@ +use std::collections::HashMap; use std::path::Path; use std::process::Command; -use color_eyre::{eyre::eyre, Result}; +use color_eyre::{{eyre::{eyre, WrapErr}, Result}, Section}; use fs_err as fs; use serde::{Deserialize, Serialize}; -use tracing::{info, warn, instrument}; +use tracing::{info, instrument, warn}; + +use kinode_process_lib::kernel_types::Erc721Metadata; use crate::KIT_CACHE; use crate::setup::{ - check_js_deps, check_py_deps, check_rust_deps, get_deps, get_newest_valid_node_version, get_python_version, - REQUIRED_PY_PACKAGE, + check_js_deps, check_py_deps, check_rust_deps, get_deps, get_newest_valid_node_version, + get_python_version, REQUIRED_PY_PACKAGE, }; +use crate::start_package::zip_directory; +use crate::view_api; const PY_VENV_NAME: &str = "process_env"; const JAVASCRIPT_SRC_PATH: &str = "src/lib.js"; const PYTHON_SRC_PATH: &str = "src/lib.py"; const RUST_SRC_PATH: &str = "src/lib.rs"; -const KINODE_WIT_URL: &str = +const KINODE_WIT_0_7_0_URL: &str = "https://raw.githubusercontent.com/kinode-dao/kinode-wit/aa2c8b11c9171b949d1991c32f58591c0e881f85/kinode.wit"; +const KINODE_WIT_0_8_0_URL: &str = + "https://raw.githubusercontent.com/kinode-dao/kinode-wit/v0.8/kinode.wit"; const WASI_VERSION: &str = "19.0.1"; // TODO: un-hardcode +const DEFAULT_WORLD_0_7_0: &str = "process"; +const DEFAULT_WORLD_0_8_0: &str = "process-v0"; #[derive(Debug, Clone, Serialize, Deserialize)] struct CargoFile { @@ -97,23 +106,75 @@ pub async fn download_file(url: &str, path: &Path) -> Result<()> { Ok(()) } +#[instrument(level = "trace", skip_all)] +pub fn read_metadata(package_dir: &Path) -> Result { + let metadata: Erc721Metadata = + serde_json::from_reader(fs::File::open(package_dir.join("metadata.json")) + .wrap_err_with(|| "Missing required metadata.json file. See discussion at https://book.kinode.org/my_first_app/chapter_1.html?highlight=metadata.json#metadatajson")? + )?; + Ok(metadata) +} + +/// Regex to dynamically capture the world name after 'world' +#[instrument(level = "trace", skip_all)] +fn extract_world(data: &str) -> Option { + let re = regex::Regex::new(r"world\s+([^\s\{]+)").unwrap(); + re.captures(data).and_then(|caps| caps.get(1).map(|match_| match_.as_str().to_string())) +} + +#[instrument(level = "trace", skip_all)] +fn extract_worlds_from_files(directory: &Path) -> Vec { + let mut worlds = vec![]; + + // Safe to return early if directory reading fails + let entries = match fs::read_dir(directory) { + Ok(entries) => entries, + Err(_) => return worlds, + }; + + for entry in entries.filter_map(Result::ok) { + let path = entry.path(); + if !path.is_file() + || Some("kinode.wit") == path.file_name().and_then(|s| s.to_str()) + || Some("wit") != path.extension().and_then(|s| s.to_str()) { + continue; + } + let contents = fs::read_to_string(&path).unwrap_or_default(); + if let Some(world) = extract_world(&contents) { + worlds.push(world); + } + } + + worlds +} + +#[instrument(level = "trace", skip_all)] +fn get_world_or_default(directory: &Path, default_world: String) -> String { + let worlds = extract_worlds_from_files(directory); + if worlds.len() == 1 { + return worlds[0].clone(); + } + warn!("Found {} worlds in {directory:?}; defaulting to {default_world}", worlds.len()); + default_world +} + #[instrument(level = "trace", skip_all)] async fn compile_javascript_wasm_process( process_dir: &Path, valid_node: Option, + world: String, verbose: bool, ) -> Result<()> { info!( "Compiling Javascript Kinode process in {:?}...", process_dir ); - let wit_dir = process_dir.join("wit"); - download_file(KINODE_WIT_URL, &wit_dir.join("kinode.wit")).await?; let wasm_file_name = process_dir.file_name().and_then(|s| s.to_str()).unwrap(); + let world_name = get_world_or_default(&process_dir.join("target").join("wit"), world); let install = "npm install".to_string(); - let componentize = format!("node componentize.mjs {wasm_file_name}"); + let componentize = format!("node componentize.mjs {wasm_file_name} {world_name}"); let (install, componentize) = valid_node .map(|valid_node| { ( @@ -154,13 +215,21 @@ async fn compile_javascript_wasm_process( async fn compile_python_wasm_process( process_dir: &Path, python: &str, + world: String, verbose: bool, ) -> Result<()> { info!("Compiling Python Kinode process in {:?}...", process_dir); - let wit_dir = process_dir.join("wit"); - download_file(KINODE_WIT_URL, &wit_dir.join("kinode.wit")).await?; let wasm_file_name = process_dir.file_name().and_then(|s| s.to_str()).unwrap(); + let world_name = get_world_or_default(&process_dir.join("target").join("wit"), world); + + let source = format!("source ../{PY_VENV_NAME}/bin/activate"); + let install = format!("pip install {REQUIRED_PY_PACKAGE}"); + let componentize = format!( + "componentize-py -d ../target/wit/ -w {} componentize lib -o ../../pkg/{}.wasm", + world_name, + wasm_file_name, + ); run_command( Command::new(python) @@ -169,10 +238,7 @@ async fn compile_python_wasm_process( verbose, )?; run_command(Command::new("bash") - .args(&[ - "-c", - &format!("source ../{PY_VENV_NAME}/bin/activate && pip install {REQUIRED_PY_PACKAGE} && componentize-py -d ../wit/ -w process componentize lib -o ../../pkg/{wasm_file_name}.wasm"), - ]) + .args(&["-c", &format!("{source} && {install} && {componentize}")]) .current_dir(process_dir.join("src")), verbose, )?; @@ -185,21 +251,19 @@ async fn compile_python_wasm_process( async fn compile_rust_wasm_process( process_dir: &Path, features: &str, + world: String, verbose: bool, ) -> Result<()> { info!("Compiling Rust Kinode process in {:?}...", process_dir); // Paths + let wit_dir = process_dir.join("target").join("wit"); let bindings_dir = process_dir .join("target") .join("bindings") .join(process_dir.file_name().unwrap()); - let wit_dir = process_dir.join("wit"); - fs::create_dir_all(&bindings_dir)?; - download_file(KINODE_WIT_URL, &wit_dir.join("kinode.wit")).await?; - // Check and download wasi_snapshot_preview1.wasm if it does not exist let wasi_snapshot_file = process_dir.join("wasi_snapshot_preview1.wasm"); let wasi_snapshot_url = format!( @@ -236,15 +300,15 @@ async fn compile_rust_wasm_process( // Build the module using Cargo let mut args = vec![ - "+nightly", - "build", - "--release", - "--no-default-features", - "--target", - "wasm32-wasi", - "--target-dir", - "target", - "--color=always" + "+nightly", + "build", + "--release", + "--no-default-features", + "--target", + "wasm32-wasi", + "--target-dir", + "target", + "--color=always" ]; if !features.is_empty() { args.push("--features"); @@ -303,7 +367,7 @@ async fn compile_rust_wasm_process( "embed", wit_dir.strip_prefix(process_dir).unwrap().to_str().unwrap(), "--world", - "process", + &world, adapted_wasm_file.to_str().unwrap(), "-o", wasm_path.to_str().unwrap(), @@ -384,10 +448,48 @@ async fn compile_package_and_ui( valid_node: Option, skip_deps_check: bool, features: &str, + url: Option, + default_world: Option, verbose: bool, ) -> Result<()> { compile_and_copy_ui(package_dir, valid_node, verbose).await?; - compile_package(package_dir, skip_deps_check, features, verbose).await?; + compile_package( + package_dir, + skip_deps_check, + features, + url, + default_world, + verbose, + ).await?; + Ok(()) +} + +#[instrument(level = "trace", skip_all)] +async fn build_wit_dir( + process_dir: &Path, + apis: &HashMap>, + wit_version: Option, +) -> Result<()> { + let wit_dir = process_dir.join("target").join("wit"); + let wit_url = match wit_version { + None => KINODE_WIT_0_7_0_URL, + Some(0) | _ => KINODE_WIT_0_8_0_URL, + }; + download_file(wit_url, &wit_dir.join("kinode.wit")).await?; + for (file_name, contents) in apis { + fs::write(wit_dir.join(file_name), contents)?; + } + + //let src_dir = process_dir.join("src"); + //for entry in src_dir.read_dir()? { + // let entry = entry?; + // let path = entry.path(); + // if Some("wit") == path.extension().and_then(|s| s.to_str()) { + // if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) { + // fs::copy(&path, wit_dir.join(file_name))?; + // } + // } + //} Ok(()) } @@ -395,35 +497,94 @@ async fn compile_package_and_ui( async fn compile_package_item( entry: std::io::Result, features: String, + apis: HashMap>, + world: String, + wit_version: Option, verbose: bool, ) -> Result<()> { let entry = entry?; let path = entry.path(); if path.is_dir() { - if path.join(RUST_SRC_PATH).exists() { - compile_rust_wasm_process(&path, &features, verbose).await?; - } else if path.join(PYTHON_SRC_PATH).exists() { + let is_rust_process = path.join(RUST_SRC_PATH).exists(); + let is_py_process = path.join(PYTHON_SRC_PATH).exists(); + let is_js_process = path.join(JAVASCRIPT_SRC_PATH).exists(); + if is_rust_process || is_py_process || is_js_process { + build_wit_dir(&path, &apis, wit_version).await?; + } + + if is_rust_process { + compile_rust_wasm_process(&path, &features, world, verbose).await?; + } else if is_py_process { let python = get_python_version(None, None)? .ok_or_else(|| eyre!("kit requires Python 3.10 or newer"))?; - compile_python_wasm_process(&path, &python, verbose).await?; - } else if path.join(JAVASCRIPT_SRC_PATH).exists() { + compile_python_wasm_process(&path, &python, world, verbose).await?; + } else if is_js_process { let valid_node = get_newest_valid_node_version(None, None)?; - compile_javascript_wasm_process(&path, valid_node, verbose).await?; + compile_javascript_wasm_process(&path, valid_node, world, verbose).await?; } } Ok(()) } +async fn fetch_dependencies( + dependencies: &Vec, + apis: &mut HashMap>, + url: String, +) -> Result<()> { + for dependency in dependencies { + let Some(zip_dir) = view_api::execute(None, Some(dependency), &url, false).await? else { + return Err(eyre!("Got unexpected result from fetching API for {dependency}")); + }; + for entry in zip_dir.read_dir()? { + let entry = entry?; + let path = entry.path(); + if Some("wit") == path.extension().and_then(|s| s.to_str()) { + let file_name = path.file_name().and_then(|s| s.to_str()).unwrap_or_default(); + let wit_contents = fs::read(&path)?; + apis.insert(file_name.into(), wit_contents); + } + } + } + Ok(()) +} + +/// package dir looks like: +/// ``` +/// metadata.json +/// api/ <- optional +/// my_package:publisher.os-v0.wit +/// pkg/ +/// api.zip <- built +/// manifest.json +/// process_i.wasm <- built +/// projess_j.wasm <- built +/// process_i/ +/// src/ +/// lib.rs +/// target/ <- built +/// api/ +/// wit/ +/// process_j/ +/// src/ +/// target/ <- built +/// api/ +/// wit/ +/// ``` + #[instrument(level = "trace", skip_all)] async fn compile_package( package_dir: &Path, skip_deps_check: bool, features: &str, + url: Option, + default_world: Option, verbose: bool, ) -> Result<()> { + let metadata = read_metadata(package_dir)?; let mut checked_rust = false; let mut checked_py = false; let mut checked_js = false; + let mut apis = HashMap::new(); for entry in package_dir.read_dir()? { let entry = entry?; let path = entry.path(); @@ -439,14 +600,71 @@ async fn compile_package( let deps = check_js_deps()?; get_deps(deps, verbose)?; checked_js = true; + } else if Some("api") == path.file_name().and_then(|s| s.to_str()) { + // zip & place in pkg/: publish API inside Kinode + let zip_path = package_dir.join("pkg").join("api.zip"); + let zip_path = zip_path.to_str().unwrap(); + zip_directory(&path, zip_path)?; + + // read api files: to be used in build + for entry in path.read_dir()? { + let entry = entry?; + let path = entry.path(); + let Some(ext) = path.extension().and_then(|e| e.to_str()) else { + continue; + }; + if ext != "wit" { + continue; + } + let Some(file_name) = path.file_name().and_then(|s| s.to_str()) else { + continue; + }; + // TODO: reenable check once deps are working + // if file_name.starts_with(&format!( + // "{}:{}", + // metadata.properties.package_name, + // metadata.properties.publisher, + // )) { + // if let Ok(api_contents) = fs::read(&path) { + // apis.insert(file_name.to_string(), api_contents); + // } + // } + if let Ok(api_contents) = fs::read(&path) { + apis.insert(file_name.to_string(), api_contents); + } + } + + // fetch dependency apis: to be used in build + if let Some(ref dependencies) = metadata.properties.dependencies { + if dependencies.is_empty() { + continue; + } + let Some(ref url) = url else { + // TODO: can we use kit-cached deps? + return Err(eyre!("Need a node to be able to fetch dependencies")); + }; + fetch_dependencies(dependencies, &mut apis, url.clone()).await?; + } } } } + let wit_world = default_world.unwrap_or_else(|| match metadata.properties.wit_version { + None => DEFAULT_WORLD_0_7_0.to_string(), + Some(0) | _ => DEFAULT_WORLD_0_8_0.to_string(), + }); + let mut tasks = tokio::task::JoinSet::new(); let features = features.to_string(); for entry in package_dir.read_dir()? { - tasks.spawn(compile_package_item(entry, features.clone(), verbose.clone())); + tasks.spawn(compile_package_item( + entry, + features.clone(), + apis.clone(), + wit_world.clone(), + metadata.properties.wit_version, + verbose.clone(), + )); } while let Some(res) = tasks.join_next().await { res??; @@ -462,6 +680,8 @@ pub async fn execute( ui_only: bool, skip_deps_check: bool, features: &str, + url: Option, + default_world: Option, verbose: bool, ) -> Result<()> { if !package_dir.join("pkg").exists() { @@ -470,9 +690,10 @@ pub async fn execute( return Ok(()); } return Err(eyre!( - "Required `pkg/` dir not found within given input dir {:?} (or cwd, if none given). Please re-run targeting a package.", + "Required `pkg/` dir not found within given input dir {:?} (or cwd, if none given).", package_dir, - )); + ).with_suggestion(|| "Please re-run targeting a package.") + ); } let ui_dir = package_dir.join("ui"); @@ -480,11 +701,25 @@ pub async fn execute( if ui_only { return Err(eyre!("kit build: can't build UI: no ui directory exists")); } else { - compile_package(package_dir, skip_deps_check, features, verbose).await + compile_package( + package_dir, + skip_deps_check, + features, + url, + default_world, + verbose, + ).await } } else { if no_ui { - return compile_package(package_dir, skip_deps_check, features, verbose).await; + return compile_package( + package_dir, + skip_deps_check, + features, + url, + default_world, + verbose, + ).await; } let deps = check_js_deps()?; @@ -499,6 +734,8 @@ pub async fn execute( valid_node, skip_deps_check, features, + url, + default_world, verbose, ).await } diff --git a/src/build_start_package/mod.rs b/src/build_start_package/mod.rs index 0ab4e634..fd13daee 100644 --- a/src/build_start_package/mod.rs +++ b/src/build_start_package/mod.rs @@ -14,9 +14,19 @@ pub async fn execute( url: &str, skip_deps_check: bool, features: &str, + default_world: Option, verbose: bool, ) -> Result<()> { - build::execute(package_dir, no_ui, ui_only, skip_deps_check, features, verbose).await?; + build::execute( + package_dir, + no_ui, + ui_only, + skip_deps_check, + features, + Some(url.into()), + default_world, + verbose, + ).await?; start_package::execute(package_dir, url).await?; Ok(()) } diff --git a/src/inject_message/mod.rs b/src/inject_message/mod.rs index 7d8bd07f..0a1fc818 100644 --- a/src/inject_message/mod.rs +++ b/src/inject_message/mod.rs @@ -140,6 +140,7 @@ pub async fn parse_response(response: reqwest::Response) -> Result { }) .ok_or_else(|| eyre!("Response did not contain `body` field."))??; + #[allow(deprecated)] let blob = data .get("lazy_load_blob") .and_then(|b| { @@ -174,12 +175,12 @@ pub async fn parse_response(response: reqwest::Response) -> Result { ))), } }) - .transpose()?; + .transpose()? + .and_then(|b| decode(b).ok()); - #[allow(deprecated)] Ok(Response { body, - lazy_load_blob_utf8: blob.clone().and_then(|b| decode(b).ok()).map(|b| String::from_utf8(b).ok()), + lazy_load_blob_utf8: blob.clone().map(|b| String::from_utf8(b).ok()), lazy_load_blob: blob, }) } diff --git a/src/lib.rs b/src/lib.rs index 388e0ad3..0edeccdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,14 @@ pub mod run_tests; pub mod setup; pub mod start_package; pub mod update; +pub mod view_api; pub const KIT_CACHE: &str = "/tmp/kinode-kit-cache"; pub const KIT_LOG_PATH_DEFAULT: &str = "/tmp/kinode-kit-cache/logs/log.log"; + +wit_bindgen::generate!({ + path: "src/run_tests/wit", + world: "tester-sys-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], +}); diff --git a/src/main.rs b/src/main.rs index a4f05aea..b767eb51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,7 @@ use kit::{ setup, start_package, update, + view_api, KIT_LOG_PATH_DEFAULT, }; @@ -202,6 +203,14 @@ async fn execute( Some(f) => f.clone(), None => "".into(), }; + let url: Option = match build_matches.get_one::("URL") { + Some(url) => Some(url.clone()), + None => { + build_matches.get_one::("NODE_PORT") + .map(|p| format!("http://localhost:{}", p)) + } + }; + let default_world = build_matches.get_one::("WORLD"); let verbose = build_matches.get_one::("VERBOSE").unwrap(); build::execute( @@ -210,6 +219,8 @@ async fn execute( *ui_only, *skip_deps_check, &features, + url, + default_world.cloned(), *verbose, ).await } @@ -233,6 +244,7 @@ async fn execute( Some(f) => f.clone(), None => "".into(), }; + let default_world = build_start_matches.get_one::("WORLD"); let verbose = build_start_matches.get_one::("VERBOSE").unwrap(); build_start_package::execute( @@ -242,6 +254,7 @@ async fn execute( &url, *skip_deps_check, &features, + default_world.cloned(), *verbose, ) .await @@ -376,6 +389,21 @@ async fn execute( update::execute(args, branch) } + Some(("view-api", view_api_matches)) => { + let package_id = view_api_matches + .get_one::("PACKAGE_ID") + .and_then(|s: &String| Some(s.as_str())); + let url: String = match view_api_matches.get_one::("URL") { + Some(url) => url.clone(), + None => { + let port = view_api_matches.get_one::("NODE_PORT").unwrap(); + format!("http://localhost:{}", port) + } + }; + + view_api::execute(None, package_id, &url, true).await?; + Ok(()) + } _ => { warn!("Invalid subcommand. Usage:\n{}", usage); Ok(()) @@ -593,6 +621,27 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .help("Pass these comma-delimited feature flags to Rust cargo builds") .required(false) ) + .arg(Arg::new("NODE_PORT") + .action(ArgAction::Set) + .short('p') + .long("port") + .help("Node port: for use on localhost (overridden by URL)") + .value_parser(value_parser!(u16)) + .required(false) + ) + .arg(Arg::new("URL") + .action(ArgAction::Set) + .short('u') + .long("url") + .help("Node URL (overrides NODE_PORT)") + .required(false) + ) + .arg(Arg::new("WORLD") + .action(ArgAction::Set) + .short('w') + .long("world") + .help("Fallback WIT world name") + ) .arg(Arg::new("VERBOSE") .action(ArgAction::SetTrue) .short('v') @@ -624,6 +673,13 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .help("Node URL (overrides NODE_PORT)") .required(false) ) + .arg(Arg::new("WORLD") + .action(ArgAction::Set) + .short('w') + .long("world") + .help("Fallback WIT world name") + .required(false) + ) .arg(Arg::new("NO_UI") .action(ArgAction::SetTrue) .long("no-ui") @@ -871,7 +927,6 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .long("url") .help("Node URL (overrides NODE_PORT)") .required(false) - //.default_value("http://localhost:8080") ) ) .subcommand(Command::new("reset-cache") @@ -934,6 +989,30 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .default_value("master") ) ) + .subcommand(Command::new("view-api") + .about("Fetch the list of APIs or a specific API") + .visible_alias("v") + .arg(Arg::new("PACKAGE_ID") + .action(ArgAction::Set) + .help("Get API of this package (default: list all APIs)") + .required(false) + ) + .arg(Arg::new("NODE_PORT") + .action(ArgAction::Set) + .short('p') + .long("port") + .help("Node port: for use on localhost (overridden by URL)") + .default_value("8080") + .value_parser(value_parser!(u16)) + ) + .arg(Arg::new("URL") + .action(ArgAction::Set) + .short('u') + .long("url") + .help("Node URL (overrides NODE_PORT)") + .required(false) + ) + ) ) } diff --git a/src/new/componentize.mjs b/src/new/componentize.mjs index 61c9dd1a..0c5a6f99 100644 --- a/src/new/componentize.mjs +++ b/src/new/componentize.mjs @@ -3,14 +3,18 @@ import { readFile, writeFile } from 'node:fs/promises'; // Retrieve the package name from command line arguments const processName = process.argv[2]; -if (!processName) { - console.error('Please provide a process name (e.g. `node componentize.mjs process_name`).'); +const worldName = process.argv[3]; +if (!processName || !worldName) { + console.error('usage:\nnode componentize.mjs processName worldName'); process.exit(1); } const jsSource = await readFile('src/lib.js', 'utf8'); -const witPath = 'wit/kinode.wit'; +const witPath = 'target/wit'; -const { component } = await componentize(jsSource, { witPath: witPath, worldName: 'process', debug: false }); +const { component } = await componentize( + jsSource, + { witPath: witPath, worldName: worldName, debug: false }, +); await writeFile(`../pkg/${processName}.wasm`, component); diff --git a/src/new/mod.rs b/src/new/mod.rs index 647afa42..e88b9b7a 100644 --- a/src/new/mod.rs +++ b/src/new/mod.rs @@ -65,10 +65,58 @@ impl From<&String> for Template { } } +fn snake_to_upper_camel_case(input: &str) -> String { + let parts: Vec<&str> = input.split('_').collect(); + let mut camel_case = String::new(); + + for part in parts { + if let Some(first_char) = part.chars().next() { + camel_case.push_str(&first_char.to_uppercase().to_string()); + camel_case.push_str(&part[first_char.len_utf8()..]); + } + } + camel_case +} + +fn replace_dots(input: &str) -> (String, String) { + let dotted = input.split('.'); + if dotted.clone().count() == 1 { + (input.to_string(), input.to_string()) + } else { + let dotted_snake = dotted + .clone() + .fold(String::new(), |mut d, item| { + if !d.is_empty() { + d.push_str("_dot_"); + } + d.push_str(item); + d + }); + let dotted_kebab = dotted + .fold(String::new(), |mut d, item| { + if !d.is_empty() { + d.push_str("-dot-"); + } + d.push_str(item); + d + }); + (dotted_snake, dotted_kebab) + } +} + fn replace_vars(input: &str, package_name: &str, publisher: &str) -> String { + let package_name_kebab = package_name.replace("_", "-"); + let (publisher_dotted_snake, publisher_dotted_kebab) = replace_dots(publisher); + let package_name_upper_camel = snake_to_upper_camel_case(package_name); + let publisher_dotted_upper_camel = snake_to_upper_camel_case(&publisher_dotted_snake); input .replace("{package_name}", package_name) + .replace("{package_name_kebab}", &package_name_kebab) + .replace("{package_name_upper_camel}", &package_name_upper_camel) .replace("{publisher}", publisher) + .replace("{publisher_dotted_snake}", &publisher_dotted_snake) + .replace("{publisher_dotted_kebab}", &publisher_dotted_kebab) + .replace("{publisher_dotted_upper_camel}", &publisher_dotted_upper_camel) .replace("Cargo.toml_", "Cargo.toml") .to_string() } @@ -152,16 +200,28 @@ pub fn execute( ui_infix, template.to_string(), ); + let test_prefix = format!( + "test/{}/", + template.to_string(), + ); let mut path_to_content: HashMap = PATH_TO_CONTENT .iter() - .filter_map(|(k, v)| { - k - .strip_prefix(&template_prefix) - .or_else(|| k.strip_prefix(&ui_prefix)) + .filter_map(|(path, content)| { + path.strip_prefix(&template_prefix) + .map(|p| p.to_string()) + .or_else(|| path.strip_prefix(&ui_prefix).map(|p| p.to_string())) + .or_else(|| path.strip_prefix(&test_prefix).map(|p| format!("test/{p}"))) + .or_else(|| { + if path.starts_with(&test_prefix) { + Some(path.to_string()) + } else { + None + } + }) .and_then(|stripped| { - let key = replace_vars(stripped, &package_name, &publisher); - let val = replace_vars(v, &package_name, &publisher); - Some((key, val)) + let modified_path = replace_vars(&stripped, &package_name, &publisher); + let modified_content = replace_vars(content, &package_name, &publisher); + Some((modified_path, modified_content)) }) }) .collect(); diff --git a/src/new/templates/javascript/no-ui/chat/api/{package_name}:{publisher}-v0.wit b/src/new/templates/javascript/no-ui/chat/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..32cee559 --- /dev/null +++ b/src/new/templates/javascript/no-ui/chat/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,27 @@ +interface {package_name_kebab} { + variant request { + send(send-request), + /// history of chat with given node + history(string), + } + + variant response { + send, + history(list), + } + + record send-request { + target: string, + message: string, + } + + record chat-message { + author: string, + content: string, + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process; +} diff --git a/src/new/templates/javascript/no-ui/chat/metadata.json b/src/new/templates/javascript/no-ui/chat/metadata.json index 675ada88..944b2bb4 100644 --- a/src/new/templates/javascript/no-ui/chat/metadata.json +++ b/src/new/templates/javascript/no-ui/chat/metadata.json @@ -9,8 +9,9 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/javascript/no-ui/chat/pkg/scripts.json b/src/new/templates/javascript/no-ui/chat/pkg/scripts.json new file mode 100644 index 00000000..ba63df9e --- /dev/null +++ b/src/new/templates/javascript/no-ui/chat/pkg/scripts.json @@ -0,0 +1,13 @@ +{ + "send.wasm": { + "root": false, + "public": false, + "request_networking": false, + "request_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ], + "grant_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ] + } +} diff --git a/src/new/templates/javascript/no-ui/chat/send/Cargo.toml_ b/src/new/templates/javascript/no-ui/chat/send/Cargo.toml_ new file mode 100644 index 00000000..efa24945 --- /dev/null +++ b/src/new/templates/javascript/no-ui/chat/send/Cargo.toml_ @@ -0,0 +1,17 @@ +[package] +name = "send" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/src/new/templates/javascript/no-ui/chat/send/src/lib.rs b/src/new/templates/javascript/no-ui/chat/send/src/lib.rs new file mode 100644 index 00000000..508f8b28 --- /dev/null +++ b/src/new/templates/javascript/no-ui/chat/send/src/lib.rs @@ -0,0 +1,46 @@ +use crate::kinode::process::{package_name}::{Request as ChatRequest, Response as ChatResponse, SendRequest}; +use kinode_process_lib::{ + await_next_message_body, call_init, println, Address, Message, Request, +}; + +wit_bindgen::generate!({ + path: "target/wit", + world: "{package_name}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], +}); + +call_init!(init); +fn init(our: Address) { + let Ok(body) = await_next_message_body() else { + println!("failed to get args!"); + return; + }; + + let args = String::from_utf8(body).unwrap_or_default(); + + let Some((target, message)) = args.split_once(" ") else { + println!("usage:\nsend:{package_name}:{publisher} target message"); + return; + }; + + let Ok(Ok(Message::Response { body, .. })) = + Request::to((our.node(), ("{package_name}", "{package_name}", "{publisher}"))) + .body( + serde_json::to_vec(&ChatRequest::Send(SendRequest { + target: target.into(), + message: message.into(), + })) + .unwrap(), + ) + .send_and_await_response(5) + else { + println!("did not receive expected Response from {package_name}:{package_name}:{publisher}"); + return; + }; + + let Ok(ChatResponse::Send) = serde_json::from_slice(&body) else { + println!("did not receive expected Ack from {package_name}:{package_name}:{publisher}"); + return; + }; +} diff --git a/src/new/templates/javascript/no-ui/chat/{package_name}/src/lib.js b/src/new/templates/javascript/no-ui/chat/{package_name}/src/lib.js index 71d49c6f..fe9c171d 100644 --- a/src/new/templates/javascript/no-ui/chat/{package_name}/src/lib.js +++ b/src/new/templates/javascript/no-ui/chat/{package_name}/src/lib.js @@ -22,6 +22,19 @@ function inputBytesToString(byteObject) { return { bytes: byteArray, string: string }; } +function addToArchive(conversation, author, content, messageArchive) { + const message = { + author: author, + content: content + }; + if (messageArchive.hasOwnProperty(conversation)) { + messageArchive[conversation].push(message); + } else { + messageArchive[conversation] = [message]; + } + return messageArchive; +} + function handleMessage(ourNode, messageArchive) { const [source, message] = receive(); @@ -35,7 +48,12 @@ function handleMessage(ourNode, messageArchive) { const { target, message: messageText } = body.Send; if (target === ourNode) { printToTerminal(0, `{package_name}|${source.node}: ${messageText}`); - messageArchive[source.node] = messageText; + messageArchive = addToArchive( + source.node, + source.node, + messageText, + messageArchive, + ); } else { sendAndAwaitResponse( { @@ -54,6 +72,12 @@ function handleMessage(ourNode, messageArchive) { }, null ); + messageArchive = addToArchive( + target, + ourNode, + messageText, + messageArchive, + ); } sendResponse( { diff --git a/src/new/templates/javascript/no-ui/echo/metadata.json b/src/new/templates/javascript/no-ui/echo/metadata.json index 675ada88..944b2bb4 100644 --- a/src/new/templates/javascript/no-ui/echo/metadata.json +++ b/src/new/templates/javascript/no-ui/echo/metadata.json @@ -9,8 +9,9 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/javascript/no-ui/fibonacci/metadata.json b/src/new/templates/javascript/no-ui/fibonacci/metadata.json index 675ada88..944b2bb4 100644 --- a/src/new/templates/javascript/no-ui/fibonacci/metadata.json +++ b/src/new/templates/javascript/no-ui/fibonacci/metadata.json @@ -9,8 +9,9 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/python/no-ui/chat/api/{package_name}:{publisher}-v0.wit b/src/new/templates/python/no-ui/chat/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..32cee559 --- /dev/null +++ b/src/new/templates/python/no-ui/chat/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,27 @@ +interface {package_name_kebab} { + variant request { + send(send-request), + /// history of chat with given node + history(string), + } + + variant response { + send, + history(list), + } + + record send-request { + target: string, + message: string, + } + + record chat-message { + author: string, + content: string, + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process; +} diff --git a/src/new/templates/python/no-ui/chat/metadata.json b/src/new/templates/python/no-ui/chat/metadata.json index 675ada88..944b2bb4 100644 --- a/src/new/templates/python/no-ui/chat/metadata.json +++ b/src/new/templates/python/no-ui/chat/metadata.json @@ -9,8 +9,9 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/python/no-ui/chat/pkg/scripts.json b/src/new/templates/python/no-ui/chat/pkg/scripts.json new file mode 100644 index 00000000..ba63df9e --- /dev/null +++ b/src/new/templates/python/no-ui/chat/pkg/scripts.json @@ -0,0 +1,13 @@ +{ + "send.wasm": { + "root": false, + "public": false, + "request_networking": false, + "request_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ], + "grant_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ] + } +} diff --git a/src/new/templates/python/no-ui/chat/send/src/lib.py b/src/new/templates/python/no-ui/chat/send/src/lib.py new file mode 100644 index 00000000..d441f955 --- /dev/null +++ b/src/new/templates/python/no-ui/chat/send/src/lib.py @@ -0,0 +1,63 @@ +import json + +import {package_name}_{publisher_dotted_snake}_v0 +from {package_name}_{publisher_dotted_snake}_v0.imports.standard import ( + Address, + MessageRequest, + MessageResponse, + ProcessId, + Request, + receive, + send_and_await_response, +) +from {package_name}_{publisher_dotted_snake}_v0.types import Err + +def parse_address(address_string): + node, _, rest = address_string.partition("@") + process, _, rest = rest.partition(":") + package, _, rest = rest.partition(":") + publisher, _, rest = rest.partition(":") + + return node, process, package, publisher + +class {package_name_upper_camel}{publisher_dotted_upper_camel}V0({package_name}_{publisher_dotted_snake}_v0.{package_name_upper_camel}{publisher_dotted_upper_camel}V0): + def init(self, our): + our_node, _, _, _ = parse_address(our) + result = receive() + if isinstance(result, Err): + raise Exception(f"{result}") + source, message = result + + match message: + case MessageResponse(): + raise Exception(f"unexpected Response: {message}") + case MessageRequest(): + args = message.value.body.decode("utf-8") + target, message = args.split() + + request = { + "Send": { + "target": target, + "message": message, + } + } + response = send_and_await_response( + Address( + our_node, + ProcessId("{package_name}", "{package_name}", "{publisher}"), + ), + Request(False, 5, json.dumps(request).encode("utf-8"), None, []), + None, + ) + if isinstance(response, Err): + raise Exception(f"{response}") + source, message = response + match message: + case MessageRequest(): + raise Exception(f"unexpected Request: {message}") + case MessageResponse(): + message = message.value + message, _ = message + body = json.loads(message.body.decode("utf-8")) + if "Send" not in body: + raise Exception(f"unexpected Response: {body}") diff --git a/src/new/templates/python/no-ui/chat/{package_name}/src/lib.py b/src/new/templates/python/no-ui/chat/{package_name}/src/lib.py index 172c95bc..45beadbc 100644 --- a/src/new/templates/python/no-ui/chat/{package_name}/src/lib.py +++ b/src/new/templates/python/no-ui/chat/{package_name}/src/lib.py @@ -1,7 +1,7 @@ import json -import process -from process.imports.standard import ( +import {package_name}_{publisher_dotted_snake}_v0 +from {package_name}_{publisher_dotted_snake}_v0.imports.standard import ( Address, MessageRequest, MessageResponse, @@ -13,7 +13,7 @@ send_and_await_response, send_response, ) -from process.types import Err +from {package_name}_{publisher_dotted_snake}_v0.types import Err def parse_address(address_string): node, _, rest = address_string.partition("@") @@ -23,10 +23,21 @@ def parse_address(address_string): return node, process, package, publisher +def add_to_archive(conversation, author, content, message_archive): + message = { + "author": author, + "content": content, + } + if conversation in message_archive: + message_archive[conversation].append(message) + else: + message_archive[conversation] = [message] + return message_archive + def handle_message(our_node, message_archive): result = receive() if isinstance(result, Err): - raise Exception(f"{result}") + raise Exception(f"got error: {result}") source, message = result match message: @@ -37,8 +48,13 @@ def handle_message(our_node, message_archive): if "Send" in body: target, message_text = body["Send"]["target"], body["Send"]["message"] if target == our_node: - print_to_terminal(0, f"{package_name}|{source.node}: {message_text}") - message_archive[source.node] = message_text + print_to_terminal(0, f"{source.node}: {message_text}") + message_archive = add_to_archive( + source.node, + source.node, + message_text, + message_archive, + ) else: send_and_await_response( Address( @@ -48,15 +64,22 @@ def handle_message(our_node, message_archive): Request(False, 5, message.value.body, None, []), None, ) + message_archive = add_to_archive( + target, + our_node, + message_text, + message_archive, + ) send_response( - Response(False, json.dumps({"Ack": None}).encode("utf-8"), None, []), + Response(False, json.dumps({"Send": None}).encode("utf-8"), None, []), None, ) elif "History" in body: + node = body["History"] send_response( Response( False, - json.dumps({"History": {"messages": message_archive}}).encode("utf-8"), + json.dumps({"History": message_archive.get(node, [])}).encode("utf-8"), None, [], ), @@ -67,7 +90,7 @@ def handle_message(our_node, message_archive): return message_archive -class Process(process.Process): +class {package_name_upper_camel}{publisher_dotted_upper_camel}V0({package_name}_{publisher_dotted_snake}_v0.{package_name_upper_camel}{publisher_dotted_upper_camel}V0): def init(self, our): print_to_terminal(0, "{package_name}: begin (python)") diff --git a/src/new/templates/python/no-ui/echo/metadata.json b/src/new/templates/python/no-ui/echo/metadata.json index 675ada88..944b2bb4 100644 --- a/src/new/templates/python/no-ui/echo/metadata.json +++ b/src/new/templates/python/no-ui/echo/metadata.json @@ -9,8 +9,9 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/python/no-ui/fibonacci/metadata.json b/src/new/templates/python/no-ui/fibonacci/metadata.json index 675ada88..944b2bb4 100644 --- a/src/new/templates/python/no-ui/fibonacci/metadata.json +++ b/src/new/templates/python/no-ui/fibonacci/metadata.json @@ -9,8 +9,9 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/rust/no-ui/chat/api/{package_name}:{publisher}-v0.wit b/src/new/templates/rust/no-ui/chat/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..c6eb7c36 --- /dev/null +++ b/src/new/templates/rust/no-ui/chat/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,27 @@ +interface {package_name_kebab} { + variant request { + send(send-request), + /// history of chat with given node + history(string), + } + + variant response { + send, + history(list), + } + + record send-request { + target: string, + message: string, + } + + record chat-message { + author: string, + content: string, + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process-v0; +} diff --git a/src/new/templates/rust/no-ui/chat/metadata.json b/src/new/templates/rust/no-ui/chat/metadata.json index 675ada88..940b56a7 100644 --- a/src/new/templates/rust/no-ui/chat/metadata.json +++ b/src/new/templates/rust/no-ui/chat/metadata.json @@ -9,8 +9,10 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "wit_version": 0, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/rust/no-ui/chat/send/Cargo.toml_ b/src/new/templates/rust/no-ui/chat/send/Cargo.toml_ index efa24945..a08adf79 100644 --- a/src/new/templates/rust/no-ui/chat/send/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/chat/send/Cargo.toml_ @@ -5,7 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/chat/send/src/lib.rs b/src/new/templates/rust/no-ui/chat/send/src/lib.rs index 66c8b349..0496bf49 100644 --- a/src/new/templates/rust/no-ui/chat/send/src/lib.rs +++ b/src/new/templates/rust/no-ui/chat/send/src/lib.rs @@ -1,28 +1,15 @@ -use serde::{Deserialize, Serialize}; - +use crate::kinode::process::{package_name}::{Request as ChatRequest, Response as ChatResponse, SendRequest}; use kinode_process_lib::{ await_next_message_body, call_init, println, Address, Message, Request, }; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -#[derive(Debug, Serialize, Deserialize)] -enum ChatRequest { - Send { target: String, message: String }, - History, -} - -#[derive(Debug, Serialize, Deserialize)] -enum ChatResponse { - Ack, - History { messages: MessageArchive }, -} - -type MessageArchive = Vec<(String, String)>; - call_init!(init); fn init(our: Address) { let Ok(body) = await_next_message_body() else { @@ -40,10 +27,10 @@ fn init(our: Address) { let Ok(Ok(Message::Response { body, .. })) = Request::to((our.node(), ("{package_name}", "{package_name}", "{publisher}"))) .body( - serde_json::to_vec(&ChatRequest::Send { + serde_json::to_vec(&ChatRequest::Send(SendRequest { target: target.into(), message: message.into(), - }) + })) .unwrap(), ) .send_and_await_response(5) @@ -52,7 +39,7 @@ fn init(our: Address) { return; }; - let Ok(ChatResponse::Ack) = serde_json::from_slice(&body) else { + let Ok(ChatResponse::Send) = serde_json::from_slice(&body) else { println!("did not receive expected Ack from {package_name}:{package_name}:{publisher}"); return; }; diff --git a/src/new/templates/rust/no-ui/chat/{package_name}/Cargo.toml_ b/src/new/templates/rust/no-ui/chat/{package_name}/Cargo.toml_ index 084992fe..c73909b5 100644 --- a/src/new/templates/rust/no-ui/chat/{package_name}/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/chat/{package_name}/Cargo.toml_ @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/chat/{package_name}/src/lib.rs b/src/new/templates/rust/no-ui/chat/{package_name}/src/lib.rs index 92c0ddc0..4550b7c9 100644 --- a/src/new/templates/rust/no-ui/chat/{package_name}/src/lib.rs +++ b/src/new/templates/rust/no-ui/chat/{package_name}/src/lib.rs @@ -1,26 +1,17 @@ -use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::str::FromStr; +use crate::kinode::process::{package_name}::{ChatMessage, Request as ChatRequest, Response as ChatResponse, SendRequest}; use kinode_process_lib::{await_message, call_init, println, Address, ProcessId, Request, Response}; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -#[derive(Debug, Serialize, Deserialize)] -enum ChatRequest { - Send { target: String, message: String }, - History, -} - -#[derive(Debug, Serialize, Deserialize)] -enum ChatResponse { - Ack, - History { messages: MessageArchive }, -} - -type MessageArchive = Vec<(String, String)>; +type MessageArchive = HashMap>; fn handle_message(our: &Address, message_archive: &mut MessageArchive) -> anyhow::Result<()> { let message = await_message()?; @@ -32,13 +23,20 @@ fn handle_message(our: &Address, message_archive: &mut MessageArchive) -> anyhow let body = message.body(); let source = message.source(); match serde_json::from_slice(body)? { - ChatRequest::Send { + ChatRequest::Send(SendRequest { ref target, ref message, - } => { + }) => { if target == &our.node { println!("{}: {}", source.node, message); - message_archive.push((source.node.clone(), message.clone())); + let message = ChatMessage { + author: source.node.clone(), + content: message.into(), + }; + message_archive + .entry(source.node.clone()) + .and_modify(|e| e.push(message.clone())) + .or_insert(vec![message]); } else { let _ = Request::new() .target(Address { @@ -50,20 +48,28 @@ fn handle_message(our: &Address, message_archive: &mut MessageArchive) -> anyhow .body(body) .send_and_await_response(5)? .unwrap(); + let message = ChatMessage { + author: our.node.clone(), + content: message.into(), + }; + message_archive + .entry(target.clone()) + .and_modify(|e| e.push(message.clone())) + .or_insert(vec![message]); } Response::new() - .body(serde_json::to_vec(&ChatResponse::Ack).unwrap()) + .body(serde_json::to_vec(&ChatResponse::Send).unwrap()) .send() .unwrap(); } - ChatRequest::History => { + ChatRequest::History(ref node) => { Response::new() - .body( - serde_json::to_vec(&ChatResponse::History { - messages: message_archive.clone(), - }) - .unwrap(), - ) + .body(serde_json::to_vec(&ChatResponse::History( + message_archive + .get(node) + .map(|msgs| msgs.clone()) + .unwrap_or_default() + )).unwrap()) .send() .unwrap(); } @@ -75,7 +81,7 @@ call_init!(init); fn init(our: Address) { println!("begin"); - let mut message_archive: MessageArchive = Vec::new(); + let mut message_archive = HashMap::new(); loop { match handle_message(&our, &mut message_archive) { diff --git a/src/new/templates/rust/no-ui/echo/metadata.json b/src/new/templates/rust/no-ui/echo/metadata.json index 675ada88..940b56a7 100644 --- a/src/new/templates/rust/no-ui/echo/metadata.json +++ b/src/new/templates/rust/no-ui/echo/metadata.json @@ -9,8 +9,10 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "wit_version": 0, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/rust/no-ui/echo/{package_name}/Cargo.toml_ b/src/new/templates/rust/no-ui/echo/{package_name}/Cargo.toml_ index 084992fe..c73909b5 100644 --- a/src/new/templates/rust/no-ui/echo/{package_name}/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/echo/{package_name}/Cargo.toml_ @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/echo/{package_name}/src/lib.rs b/src/new/templates/rust/no-ui/echo/{package_name}/src/lib.rs index 9e02fb06..9b0137fe 100644 --- a/src/new/templates/rust/no-ui/echo/{package_name}/src/lib.rs +++ b/src/new/templates/rust/no-ui/echo/{package_name}/src/lib.rs @@ -1,8 +1,8 @@ use kinode_process_lib::{await_message, call_init, println, Address, Response}; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "process-v0", }); fn handle_message(_our: &Address) -> anyhow::Result<()> { diff --git a/src/new/templates/rust/no-ui/fibonacci/api/{package_name}:{publisher}-v0.wit b/src/new/templates/rust/no-ui/fibonacci/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..73615f3f --- /dev/null +++ b/src/new/templates/rust/no-ui/fibonacci/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,16 @@ +interface {package_name_kebab} { + variant request { + number(u32), + numbers(tuple), + } + + variant response { + number(u64), + numbers(tuple), + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process-v0; +} diff --git a/src/new/templates/rust/no-ui/fibonacci/metadata.json b/src/new/templates/rust/no-ui/fibonacci/metadata.json index 675ada88..940b56a7 100644 --- a/src/new/templates/rust/no-ui/fibonacci/metadata.json +++ b/src/new/templates/rust/no-ui/fibonacci/metadata.json @@ -9,8 +9,10 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "wit_version": 0, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/rust/no-ui/fibonacci/number/Cargo.toml_ b/src/new/templates/rust/no-ui/fibonacci/number/Cargo.toml_ index fb7cb040..27b23008 100644 --- a/src/new/templates/rust/no-ui/fibonacci/number/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/fibonacci/number/Cargo.toml_ @@ -5,7 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/fibonacci/number/src/lib.rs b/src/new/templates/rust/no-ui/fibonacci/number/src/lib.rs index ba0ede9f..324f5cdf 100644 --- a/src/new/templates/rust/no-ui/fibonacci/number/src/lib.rs +++ b/src/new/templates/rust/no-ui/fibonacci/number/src/lib.rs @@ -1,29 +1,18 @@ -use serde::{Deserialize, Serialize}; - +use crate::kinode::process::{package_name}::{Request as FibonacciRequest, Response as FibonacciResponse}; use kinode_process_lib::{ - await_next_request_body, call_init, println, Address, Message, Request, + await_next_message_body, call_init, println, Address, Message, Request, }; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -#[derive(Debug, Serialize, Deserialize)] -enum FibonacciRequest { - Number(u32), - Numbers((u32, u32)), -} - -#[derive(Debug, Serialize, Deserialize)] -enum FibonacciResponse { - Number(u128), - Numbers((u128, u32)), -} - call_init!(init); fn init(our: Address) { - let Ok(body) = await_next_request_body() else { + let Ok(body) = await_next_message_body() else { println!("failed to get args!"); return; }; diff --git a/src/new/templates/rust/no-ui/fibonacci/{package_name}/Cargo.toml_ b/src/new/templates/rust/no-ui/fibonacci/{package_name}/Cargo.toml_ index 084992fe..c73909b5 100644 --- a/src/new/templates/rust/no-ui/fibonacci/{package_name}/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/fibonacci/{package_name}/Cargo.toml_ @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/fibonacci/{package_name}/src/lib.rs b/src/new/templates/rust/no-ui/fibonacci/{package_name}/src/lib.rs index e57f072f..89b6952c 100644 --- a/src/new/templates/rust/no-ui/fibonacci/{package_name}/src/lib.rs +++ b/src/new/templates/rust/no-ui/fibonacci/{package_name}/src/lib.rs @@ -1,27 +1,17 @@ +use crate::kinode::process::{package_name}::{Request as FibonacciRequest, Response as FibonacciResponse}; use kinode_process_lib::{await_message, call_init, println, Address, Response}; -use serde::{Deserialize, Serialize}; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -#[derive(Debug, Serialize, Deserialize)] -enum FibonacciRequest { - Number(u32), - Numbers((u32, u32)), -} - -#[derive(Debug, Serialize, Deserialize)] -enum FibonacciResponse { - Number(u128), - Numbers((u128, u32)), -} - /// calculate the nth Fibonacci number -/// since we are using u128, the maximum number -/// we can calculate is the 186th Fibonacci number -fn fibonacci(n: u32) -> u128 { +/// since we are using u64, the maximum number +/// we can calculate is the 93rd Fibonacci number +fn fibonacci(n: u32) -> u64 { if n == 0 { return 0; } diff --git a/src/new/templates/rust/no-ui/file_transfer/api/{package_name}:{publisher}-v0.wit b/src/new/templates/rust/no-ui/file_transfer/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..3ae9a97c --- /dev/null +++ b/src/new/templates/rust/no-ui/file_transfer/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,53 @@ +interface {package_name_kebab} { + use standard.{address}; + + variant request { + list-files, + download(download-request), + progress(progress-request), + } + + variant response { + list-files(list), + download, + done, + started, + } + + variant worker-request { + initialize(initialize-request), + chunk(chunk-request), + size(u64), + } + + record download-request { + name: string, + target: address, + } + + record progress-request { + name: string, + progress: u64, + } + + record file-info { + name: string, + size: u64, + } + + record initialize-request { + name: string, + target-worker: option
, + } + + record chunk-request { + name: string, + offset: u64, + length: u64, + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process-v0; +} diff --git a/src/new/templates/rust/no-ui/file_transfer/download/Cargo.toml_ b/src/new/templates/rust/no-ui/file_transfer/download/Cargo.toml_ index dcc06a91..9355a413 100644 --- a/src/new/templates/rust/no-ui/file_transfer/download/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/file_transfer/download/Cargo.toml_ @@ -5,7 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/file_transfer/download/src/lib.rs b/src/new/templates/rust/no-ui/file_transfer/download/src/lib.rs index 33afd7ec..5a10f6c4 100644 --- a/src/new/templates/rust/no-ui/file_transfer/download/src/lib.rs +++ b/src/new/templates/rust/no-ui/file_transfer/download/src/lib.rs @@ -1,45 +1,45 @@ -use serde::{Deserialize, Serialize}; - +use crate::kinode::process::standard::{ProcessId as WitProcessId}; +use crate::kinode::process::{package_name}::{Address as WitAddress, Request as TransferRequest, DownloadRequest}; use kinode_process_lib::{ - await_next_request_body, call_init, println, Address, Message, Request, + await_next_message_body, call_init, println, Address, Message, ProcessId, Request, }; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -#[derive(Serialize, Deserialize, Debug)] -pub enum TransferRequest { - ListFiles, - Download { name: String, target: Address }, - Progress { name: String, progress: u64 }, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum TransferResponse { - ListFiles(Vec), - Download { name: String, worker: Address }, - Done, - Started, +impl From
for WitAddress { + fn from(address: Address) -> Self { + WitAddress { + node: address.node, + process: address.process.into(), + } + } } -#[derive(Serialize, Deserialize, Debug)] -pub struct FileInfo { - pub name: String, - pub size: u64, +impl From for WitProcessId { + fn from(process: ProcessId) -> Self { + WitProcessId { + process_name: process.process_name, + package_name: process.package_name, + publisher_node: process.publisher_node, + } + } } call_init!(init); fn init(our: Address) { - let Ok(body) = await_next_request_body() else { + let Ok(body) = await_next_message_body() else { println!("failed to get args!"); return; }; let args = String::from_utf8(body).unwrap_or_default(); let Some((name, who)) = args.split_once(" ") else { - println!("usage: {}@download:{package_name}:{publisher} file_name who", our.node()); + println!("usage: download:{package_name}:{publisher} file_name who"); return }; let our: Address = format!("{}@{package_name}:{package_name}:{publisher}", our.node()) @@ -52,13 +52,13 @@ fn init(our: Address) { let Ok(Ok(Message::Response { .. })) = Request::to(our) - .body(serde_json::to_vec(&TransferRequest::Download { + .body(serde_json::to_vec(&TransferRequest::Download(DownloadRequest { name: name.into(), - target: target.clone(), - }).unwrap()) + target: target.clone().into(), + })).unwrap()) .send_and_await_response(5) else { - println!("did not receive expected Response from {target}"); + println!("download: did not receive expected Response from {target:?}"); return; }; } diff --git a/src/new/templates/rust/no-ui/file_transfer/list_files/Cargo.toml_ b/src/new/templates/rust/no-ui/file_transfer/list_files/Cargo.toml_ index 7eb0aad6..68392bdf 100644 --- a/src/new/templates/rust/no-ui/file_transfer/list_files/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/file_transfer/list_files/Cargo.toml_ @@ -5,7 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/file_transfer/list_files/src/lib.rs b/src/new/templates/rust/no-ui/file_transfer/list_files/src/lib.rs index 19b0ea44..2ac66a02 100644 --- a/src/new/templates/rust/no-ui/file_transfer/list_files/src/lib.rs +++ b/src/new/templates/rust/no-ui/file_transfer/list_files/src/lib.rs @@ -1,45 +1,25 @@ -use serde::{Deserialize, Serialize}; - +use crate::kinode::process::{package_name}::{Request as TransferRequest, Response as TransferResponse}; use kinode_process_lib::{ - await_next_request_body, call_init, println, Address, Message, Request, + await_next_message_body, call_init, println, Address, Message, Request, }; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -#[derive(Serialize, Deserialize, Debug)] -pub enum TransferRequest { - ListFiles, - Download { name: String, target: Address }, - Progress { name: String, progress: u64 }, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum TransferResponse { - ListFiles(Vec), - Download { name: String, worker: Address }, - Done, - Started, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct FileInfo { - pub name: String, - pub size: u64, -} - call_init!(init); -fn init(our: Address) { - let Ok(body) = await_next_request_body() else { +fn init(_our: Address) { + let Ok(body) = await_next_message_body() else { println!("failed to get args!"); return; }; let who = String::from_utf8(body).unwrap_or_default(); if who.is_empty() { - println!("usage: {}@list_files:{package_name}:{publisher} who", our); + println!("usage: list_files:{package_name}:{publisher} who"); return; } diff --git a/src/new/templates/rust/no-ui/file_transfer/metadata.json b/src/new/templates/rust/no-ui/file_transfer/metadata.json index f621b7d7..940b56a7 100644 --- a/src/new/templates/rust/no-ui/file_transfer/metadata.json +++ b/src/new/templates/rust/no-ui/file_transfer/metadata.json @@ -9,7 +9,9 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "wit_version": 0, + "dependencies": [] }, "external_url": "", "animation_url": "" diff --git a/src/new/templates/rust/no-ui/file_transfer/worker/Cargo.toml_ b/src/new/templates/rust/no-ui/file_transfer/worker/Cargo.toml_ index f5aaef3d..65d818ba 100644 --- a/src/new/templates/rust/no-ui/file_transfer/worker/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/file_transfer/worker/Cargo.toml_ @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib.git", tag = "v0.6.0" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/no-ui/file_transfer/worker/src/lib.rs b/src/new/templates/rust/no-ui/file_transfer/worker/src/lib.rs index 10818a42..4eb9f0d2 100644 --- a/src/new/templates/rust/no-ui/file_transfer/worker/src/lib.rs +++ b/src/new/templates/rust/no-ui/file_transfer/worker/src/lib.rs @@ -1,6 +1,7 @@ -use serde::{Deserialize, Serialize}; use std::str::FromStr; +use crate::kinode::process::standard::{ProcessId as WitProcessId}; +use crate::kinode::process::{package_name}::{Address as WitAddress, Request as TransferRequest, WorkerRequest, ProgressRequest, InitializeRequest, ChunkRequest}; use kinode_process_lib::{ await_message, call_init, get_blob, println, vfs::{open_dir, open_file, Directory, File, SeekFrom}, @@ -8,33 +9,51 @@ use kinode_process_lib::{ }; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -const CHUNK_SIZE: u64 = 1048576; // 1MB +impl From
for WitAddress { + fn from(address: Address) -> Self { + WitAddress { + node: address.node, + process: address.process.into(), + } + } +} -#[derive(Serialize, Deserialize, Debug)] -pub enum WorkerRequest { - Initialize { - name: String, - target_worker: Option
, - }, - Chunk { - name: String, - offset: u64, - length: u64, - }, - Size(u64), +impl From for WitProcessId { + fn from(process: ProcessId) -> Self { + WitProcessId { + process_name: process.process_name, + package_name: process.package_name, + publisher_node: process.publisher_node, + } + } +} +impl From for Address { + fn from(address: WitAddress) -> Self { + Address { + node: address.node, + process: address.process.into(), + } + } } -#[derive(Serialize, Deserialize, Debug)] -pub enum TransferRequest { - ListFiles, - Download { name: String, target: Address }, - Progress { name: String, progress: u64 }, +impl From for ProcessId { + fn from(process: WitProcessId) -> Self { + ProcessId { + process_name: process.process_name, + package_name: process.package_name, + publisher_node: process.publisher_node, + } + } } +const CHUNK_SIZE: u64 = 1048576; // 1MB + fn handle_message( our: &Address, file: &mut Option, @@ -48,13 +67,11 @@ fn handle_message( ref body, .. } => { - let request = serde_json::from_slice::(body)?; - - match request { - WorkerRequest::Initialize { + match serde_json::from_slice(body)? { + WorkerRequest::Initialize(InitializeRequest { name, target_worker, - } => { + }) => { // initialize command from main process, // sets up worker, matches on if it's a sender or receiver. // target_worker = None, we are receiver, else sender. @@ -65,6 +82,7 @@ fn handle_message( match target_worker { Some(target_worker) => { + let target_worker: Address = target_worker.into(); // we have a target, chunk the data, and send it. let size = active_file.metadata()?.len; let num_chunks = (size as f64 / CHUNK_SIZE as f64).ceil() as u64; @@ -85,11 +103,11 @@ fn handle_message( active_file.read_at(&mut buffer)?; Request::new() - .body(serde_json::to_vec(&WorkerRequest::Chunk { + .body(serde_json::to_vec(&WorkerRequest::Chunk(ChunkRequest { name: name.clone(), offset, length, - })?) + }))?) .target(target_worker.clone()) .blob_bytes(buffer) .send()?; @@ -107,11 +125,11 @@ fn handle_message( } } // someone sending a chunk to us! - WorkerRequest::Chunk { + WorkerRequest::Chunk(ChunkRequest { name, offset, length, - } => { + }) => { let file = match file { Some(file) => file, None => { @@ -143,10 +161,10 @@ fn handle_message( }; Request::new() - .body(serde_json::to_vec(&TransferRequest::Progress { + .body(serde_json::to_vec(&TransferRequest::Progress(ProgressRequest { name, progress, - })?) + }))?) .target(&main_app) .send()?; diff --git a/src/new/templates/rust/no-ui/file_transfer/{package_name}/Cargo.toml_ b/src/new/templates/rust/no-ui/file_transfer/{package_name}/Cargo.toml_ index 64320709..660e74db 100644 --- a/src/new/templates/rust/no-ui/file_transfer/{package_name}/Cargo.toml_ +++ b/src/new/templates/rust/no-ui/file_transfer/{package_name}/Cargo.toml_ @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib.git", tag = "v0.6.0" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } multipart = "0.18.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src/new/templates/rust/no-ui/file_transfer/{package_name}/src/lib.rs b/src/new/templates/rust/no-ui/file_transfer/{package_name}/src/lib.rs index 01617259..06b9b17a 100644 --- a/src/new/templates/rust/no-ui/file_transfer/{package_name}/src/lib.rs +++ b/src/new/templates/rust/no-ui/file_transfer/{package_name}/src/lib.rs @@ -1,42 +1,53 @@ +use crate::kinode::process::standard::{ProcessId as WitProcessId}; +use crate::kinode::process::{package_name}::{Address as WitAddress, Request as TransferRequest, Response as TransferResponse, WorkerRequest, DownloadRequest, ProgressRequest, FileInfo, InitializeRequest}; use kinode_process_lib::{ await_message, call_init, our_capabilities, println, spawn, vfs::{create_drive, metadata, open_dir, Directory, FileType}, - Address, OnExit, Request, Response, + Address, OnExit, ProcessId, Request, Response, }; -use serde::{Deserialize, Serialize}; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "{package_name_kebab}-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize], }); -#[derive(Serialize, Deserialize, Debug)] -pub enum TransferRequest { - ListFiles, - Download { name: String, target: Address }, - Progress { name: String, progress: u64 }, +impl From
for WitAddress { + fn from(address: Address) -> Self { + WitAddress { + node: address.node, + process: address.process.into(), + } + } } -#[derive(Serialize, Deserialize, Debug)] -pub enum TransferResponse { - ListFiles(Vec), - Download { name: String, worker: Address }, - Done, - Started, +impl From for WitProcessId { + fn from(process: ProcessId) -> Self { + WitProcessId { + process_name: process.process_name, + package_name: process.package_name, + publisher_node: process.publisher_node, + } + } } - -#[derive(Serialize, Deserialize, Debug)] -pub struct FileInfo { - pub name: String, - pub size: u64, +impl From for Address { + fn from(address: WitAddress) -> Self { + Address { + node: address.node, + process: address.process.into(), + } + } } -#[derive(Serialize, Deserialize, Debug)] -pub enum WorkerRequest { - Initialize { - name: String, - target_worker: Option
, - }, +impl From for ProcessId { + fn from(process: WitProcessId) -> Self { + ProcessId { + process_name: process.process_name, + package_name: process.package_name, + publisher_node: process.publisher_node, + } + } } fn ls_files(files_dir: &Directory) -> anyhow::Result> { @@ -64,9 +75,7 @@ fn handle_transfer_request( body: &[u8], files_dir: &Directory, ) -> anyhow::Result<()> { - let transfer_request = serde_json::from_slice::(body)?; - - match transfer_request { + match serde_json::from_slice(body)? { TransferRequest::ListFiles => { let files = ls_files(files_dir)?; @@ -74,7 +83,7 @@ fn handle_transfer_request( .body(serde_json::to_vec(&TransferResponse::ListFiles(files))?) .send()?; } - TransferRequest::Download { name, target } => { + TransferRequest::Download(DownloadRequest { name, target }) => { // spin up a worker, initialize based on whether it's a downloader or a sender. let our_worker = spawn( None, @@ -93,33 +102,33 @@ fn handle_transfer_request( if source.node == our.node { // we want to download a file let _resp = Request::new() - .body(serde_json::to_vec(&WorkerRequest::Initialize { + .body(serde_json::to_vec(&WorkerRequest::Initialize(InitializeRequest { name: name.clone(), target_worker: None, - })?) + }))?) .target(&our_worker_address) .send_and_await_response(5)??; // send our initialized worker address to the other node Request::new() - .body(serde_json::to_vec(&TransferRequest::Download { + .body(serde_json::to_vec(&TransferRequest::Download(DownloadRequest { name: name.clone(), - target: our_worker_address, - })?) - .target(&target) + target: our_worker_address.into(), + }))?) + .target::
(target.clone().into()) .send()?; } else { // they want to download a file Request::new() - .body(serde_json::to_vec(&WorkerRequest::Initialize { + .body(serde_json::to_vec(&WorkerRequest::Initialize(InitializeRequest { name: name.clone(), target_worker: Some(target), - })?) + }))?) .target(&our_worker_address) .send()?; } } - TransferRequest::Progress { name, progress } => { + TransferRequest::Progress(ProgressRequest { name, progress }) => { println!("{} progress: {}%", name, progress); } } diff --git a/src/new/templates/rust/ui/chat/metadata.json b/src/new/templates/rust/ui/chat/metadata.json index 675ada88..940b56a7 100644 --- a/src/new/templates/rust/ui/chat/metadata.json +++ b/src/new/templates/rust/ui/chat/metadata.json @@ -9,8 +9,10 @@ "mirrors": [], "code_hashes": { "0.1.0": "" - } + }, + "wit_version": 0, + "dependencies": [] }, "external_url": "", "animation_url": "" -} \ No newline at end of file +} diff --git a/src/new/templates/rust/ui/chat/{package_name}/Cargo.toml_ b/src/new/templates/rust/ui/chat/{package_name}/Cargo.toml_ index 084992fe..c73909b5 100644 --- a/src/new/templates/rust/ui/chat/{package_name}/Cargo.toml_ +++ b/src/new/templates/rust/ui/chat/{package_name}/Cargo.toml_ @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/src/new/templates/rust/ui/chat/{package_name}/src/lib.rs b/src/new/templates/rust/ui/chat/{package_name}/src/lib.rs index 78760a4b..5522ece5 100644 --- a/src/new/templates/rust/ui/chat/{package_name}/src/lib.rs +++ b/src/new/templates/rust/ui/chat/{package_name}/src/lib.rs @@ -12,8 +12,8 @@ use kinode_process_lib::{ use serde::{Deserialize, Serialize}; wit_bindgen::generate!({ - path: "wit", - world: "process", + path: "target/wit", + world: "process-v0", }); #[derive(Debug, Serialize, Deserialize)] diff --git a/src/new/templates/test/chat/tests.toml b/src/new/templates/test/chat/tests.toml new file mode 100644 index 00000000..4dbc9824 --- /dev/null +++ b/src/new/templates/test/chat/tests.toml @@ -0,0 +1,24 @@ +runtime = { FetchVersion = "latest" } +# runtime = { RepoPath = "~/git/kinode" } +runtime_build_release = false + + +[[tests]] +setup_package_paths = [".."] +test_packages = [ + { path = "{package_name}_test", grant_capabilities = ["{package_name}:{package_name}:{publisher}"] }, +] +timeout_secs = 5 +fakechain_router = 8545 + +[[tests.nodes]] +port = 8080 +home = "home/first" +fake_node_name = "first.dev" +runtime_verbosity = 0 + +[[tests.nodes]] +port = 8081 +home = "home/second" +fake_node_name = "second.dev" +runtime_verbosity = 0 diff --git a/src/new/templates/test/chat/{package_name}_test/Cargo.toml_ b/src/new/templates/test/chat/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..6c797c34 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/Cargo.toml_ @@ -0,0 +1,10 @@ +[workspace] +resolver = "2" +members = [ + "{package_name}_test", +] + +[profile.release] +panic = "abort" +opt-level = "s" +lto = true diff --git a/src/new/templates/test/chat/{package_name}_test/api/tester:sys-v0.wit b/src/new/templates/test/chat/{package_name}_test/api/tester:sys-v0.wit new file mode 100644 index 00000000..7fe0574b --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/api/tester:sys-v0.wit @@ -0,0 +1,27 @@ +interface tester { + variant request { + run(run-request), + } + + variant response { + run(result<_, fail-response>) + } + + record run-request { + input-node-names: list, + test-names: list, + test-timeout: u64, + } + + record fail-response { + test: string, + file: string, + line: u32, + column: u32, + } +} + +world tester-sys-v0 { + import tester; + include process-v0; +} diff --git a/src/new/templates/test/chat/{package_name}_test/api/{package_name}:{publisher}-v0.wit b/src/new/templates/test/chat/{package_name}_test/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..c6eb7c36 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,27 @@ +interface {package_name_kebab} { + variant request { + send(send-request), + /// history of chat with given node + history(string), + } + + variant response { + send, + history(list), + } + + record send-request { + target: string, + message: string, + } + + record chat-message { + author: string, + content: string, + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process-v0; +} diff --git a/src/new/templates/test/chat/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit b/src/new/templates/test/chat/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit new file mode 100644 index 00000000..79c71980 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit @@ -0,0 +1,5 @@ +world {package_name_kebab}-test-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + import tester; + include process-v0; +} diff --git a/src/new/templates/test/chat/{package_name}_test/metadata.json b/src/new/templates/test/chat/{package_name}_test/metadata.json new file mode 100644 index 00000000..cea44761 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/metadata.json @@ -0,0 +1,17 @@ +{ + "name": "{package_name} Test", + "description": "A test for {package_name}.", + "image": "", + "properties": { + "package_name": "{package_name}_test", + "current_version": "0.1.0", + "publisher": "{publisher}", + "mirrors": [], + "code_hashes": { + "0.1.0": "" + }, + "wit_version": 0 + }, + "external_url": "", + "animation_url": "" +} diff --git a/src/new/templates/test/chat/{package_name}_test/pkg/manifest.json b/src/new/templates/test/chat/{package_name}_test/pkg/manifest.json new file mode 100644 index 00000000..61ef3388 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/pkg/manifest.json @@ -0,0 +1,13 @@ +[ + { + "process_name": "{package_name}_test", + "process_wasm_path": "/{package_name}_test.wasm", + "on_exit": "Restart", + "request_networking": false, + "request_capabilities": [], + "grant_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ], + "public": true + } +] diff --git a/src/new/templates/test/chat/{package_name}_test/{package_name}_test/Cargo.toml_ b/src/new/templates/test/chat/{package_name}_test/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..402919e0 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/{package_name}_test/Cargo.toml_ @@ -0,0 +1,21 @@ +[package] +name = "{package_name}_test" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +bincode = "1.3" +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "b492f3b" } +process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" } +rmp-serde = "1.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/src/new/templates/test/chat/{package_name}_test/{package_name}_test/src/lib.rs b/src/new/templates/test/chat/{package_name}_test/{package_name}_test/src/lib.rs new file mode 100644 index 00000000..c0637710 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/{package_name}_test/src/lib.rs @@ -0,0 +1,107 @@ +use crate::kinode::process::{package_name}::{ChatMessage, Request as ChatRequest, Response as ChatResponse, SendRequest}; +use crate::kinode::process::tester::{Request as TesterRequest, Response as TesterResponse, RunRequest, FailResponse}; + +use kinode_process_lib::{await_message, call_init, print_to_terminal, println, Address, ProcessId, Request, Response}; + +mod tester_lib; + +wit_bindgen::generate!({ + path: "target/wit", + world: "{package_name_kebab}-test-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], +}); + +fn handle_message (our: &Address) -> anyhow::Result<()> { + let message = await_message().unwrap(); + + if !message.is_request() { + unimplemented!(); + } + let source = message.source(); + if our.node != source.node { + return Err(anyhow::anyhow!( + "rejecting foreign Message from {:?}", + source, + )); + } + let TesterRequest::Run(RunRequest { + input_node_names: node_names, + .. + }) = message.body().try_into()?; + print_to_terminal(0, "{package_name}_test: a"); + assert!(node_names.len() >= 2); + if our.node != node_names[0] { + // we are not master node: return + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + return Ok(()); + } + + // we are master node + + let our_chat_address = Address { + node: our.node.clone(), + process: ProcessId::new(Some("{package_name}"), "{package_name}", "{publisher}"), + }; + let their_chat_address = Address { + node: node_names[1].clone(), + process: ProcessId::new(Some("{package_name}"), "{package_name}", "{publisher}"), + }; + + // Send + print_to_terminal(0, "{package_name}_test: b"); + let message: String = "hello".into(); + let _ = Request::new() + .target(our_chat_address.clone()) + .body(ChatRequest::Send(SendRequest { + target: node_names[1].clone(), + message: message.clone(), + })) + .send_and_await_response(15)?.unwrap(); + + // Get history from receiver & test + print_to_terminal(0, "{package_name}_test: c"); + let response = Request::new() + .target(their_chat_address.clone()) + .body(ChatRequest::History(our.node.clone())) + .send_and_await_response(15)?.unwrap(); + if response.is_request() { fail!("{package_name}_test"); }; + let ChatResponse::History(messages) = response.body().try_into()? else { + fail!("{package_name}_test"); + }; + let expected_messages = vec![ChatMessage { + author: our.node.clone(), + content: message, + }]; + + if messages != expected_messages { + println!("{messages:?} != {expected_messages:?}"); + fail!("{package_name}_test"); + } + + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + + Ok(()) +} + +call_init!(init); +fn init(our: Address) { + print_to_terminal(0, "begin"); + + loop { + match handle_message(&our) { + Ok(()) => {}, + Err(e) => { + print_to_terminal(0, format!("{package_name}_test: error: {e:?}").as_str()); + + fail!("{package_name}_test"); + }, + }; + } +} diff --git a/src/new/templates/test/chat/{package_name}_test/{package_name}_test/src/tester_lib.rs b/src/new/templates/test/chat/{package_name}_test/{package_name}_test/src/tester_lib.rs new file mode 100644 index 00000000..9b367d36 --- /dev/null +++ b/src/new/templates/test/chat/{package_name}_test/{package_name}_test/src/tester_lib.rs @@ -0,0 +1,31 @@ +use crate::kinode::process::tester::{ + Response as TesterResponse, FailResponse, +}; + +#[macro_export] +macro_rules! fail { + ($test:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: file!().into(), + line: line!(), + column: column!(), + }))) + .send() + .unwrap(); + panic!("") + }; + ($test:expr, $file:expr, $line:expr, $column:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: $file.into(), + line: $line, + column: $column, + }))) + .send() + .unwrap(); + panic!("") + }; +} diff --git a/src/new/templates/test/echo/tests.toml b/src/new/templates/test/echo/tests.toml new file mode 100644 index 00000000..e1172f01 --- /dev/null +++ b/src/new/templates/test/echo/tests.toml @@ -0,0 +1,18 @@ +runtime = { FetchVersion = "latest" } +# runtime = { RepoPath = "~/git/kinode" } +runtime_build_release = false + + +[[tests]] +setup_package_paths = [".."] +test_packages = [ + { path = "{package_name}_test", grant_capabilities = ["{package_name}:{package_name}:{publisher}"] }, +] +timeout_secs = 5 +fakechain_router = 8545 + +[[tests.nodes]] +port = 8080 +home = "home/first" +fake_node_name = "first.dev" +runtime_verbosity = 0 diff --git a/src/new/templates/test/echo/{package_name}_test/Cargo.toml_ b/src/new/templates/test/echo/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..6c797c34 --- /dev/null +++ b/src/new/templates/test/echo/{package_name}_test/Cargo.toml_ @@ -0,0 +1,10 @@ +[workspace] +resolver = "2" +members = [ + "{package_name}_test", +] + +[profile.release] +panic = "abort" +opt-level = "s" +lto = true diff --git a/src/new/templates/test/echo/{package_name}_test/api/tester:sys-v0.wit b/src/new/templates/test/echo/{package_name}_test/api/tester:sys-v0.wit new file mode 100644 index 00000000..7fe0574b --- /dev/null +++ b/src/new/templates/test/echo/{package_name}_test/api/tester:sys-v0.wit @@ -0,0 +1,27 @@ +interface tester { + variant request { + run(run-request), + } + + variant response { + run(result<_, fail-response>) + } + + record run-request { + input-node-names: list, + test-names: list, + test-timeout: u64, + } + + record fail-response { + test: string, + file: string, + line: u32, + column: u32, + } +} + +world tester-sys-v0 { + import tester; + include process-v0; +} diff --git a/src/new/templates/test/echo/{package_name}_test/metadata.json b/src/new/templates/test/echo/{package_name}_test/metadata.json new file mode 100644 index 00000000..cea44761 --- /dev/null +++ b/src/new/templates/test/echo/{package_name}_test/metadata.json @@ -0,0 +1,17 @@ +{ + "name": "{package_name} Test", + "description": "A test for {package_name}.", + "image": "", + "properties": { + "package_name": "{package_name}_test", + "current_version": "0.1.0", + "publisher": "{publisher}", + "mirrors": [], + "code_hashes": { + "0.1.0": "" + }, + "wit_version": 0 + }, + "external_url": "", + "animation_url": "" +} diff --git a/src/new/templates/test/echo/{package_name}_test/pkg/manifest.json b/src/new/templates/test/echo/{package_name}_test/pkg/manifest.json new file mode 100644 index 00000000..61ef3388 --- /dev/null +++ b/src/new/templates/test/echo/{package_name}_test/pkg/manifest.json @@ -0,0 +1,13 @@ +[ + { + "process_name": "{package_name}_test", + "process_wasm_path": "/{package_name}_test.wasm", + "on_exit": "Restart", + "request_networking": false, + "request_capabilities": [], + "grant_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ], + "public": true + } +] diff --git a/src/new/templates/test/echo/{package_name}_test/{package_name}_test/Cargo.toml_ b/src/new/templates/test/echo/{package_name}_test/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..402919e0 --- /dev/null +++ b/src/new/templates/test/echo/{package_name}_test/{package_name}_test/Cargo.toml_ @@ -0,0 +1,21 @@ +[package] +name = "{package_name}_test" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +bincode = "1.3" +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "b492f3b" } +process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" } +rmp-serde = "1.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/src/new/templates/test/echo/{package_name}_test/{package_name}_test/src/lib.rs b/src/new/templates/test/echo/{package_name}_test/{package_name}_test/src/lib.rs new file mode 100644 index 00000000..304ca069 --- /dev/null +++ b/src/new/templates/test/echo/{package_name}_test/{package_name}_test/src/lib.rs @@ -0,0 +1,75 @@ +use crate::kinode::process::tester::{Request as TesterRequest, Response as TesterResponse, RunRequest, FailResponse}; + +use kinode_process_lib::{await_message, call_init, print_to_terminal, println, Address, ProcessId, Request, Response}; + +mod tester_lib; + +wit_bindgen::generate!({ + path: "target/wit", + world: "tester-sys-v0", + generate_unused_types: true, + additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], +}); + +fn handle_message (our: &Address) -> anyhow::Result<()> { + let message = await_message().unwrap(); + + if !message.is_request() { + unimplemented!(); + } + let source = message.source(); + if our.node != source.node { + return Err(anyhow::anyhow!( + "rejecting foreign Message from {:?}", + source, + )); + } + let TesterRequest::Run(RunRequest { + input_node_names: node_names, + .. + }) = message.body().try_into()?; + print_to_terminal(0, "{package_name}_test: a"); + assert!(node_names.len() == 1); + + let our_echo_address = Address { + node: our.node.clone(), + process: ProcessId::new(Some("{package_name}"), "{package_name}", "{publisher}"), + }; + + // Send + print_to_terminal(0, "{package_name}_test: b"); + let message: String = "hello".into(); + let response = Request::new() + .target(our_echo_address) + .body(serde_json::to_vec("test")?) + .send_and_await_response(15)?.unwrap(); + if response.is_request() { fail!("{package_name}_test"); }; + if serde_json::json!("Ack") != serde_json::from_slice::( + response.body() + )? { + fail!("{package_name}_test"); + }; + + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + + Ok(()) +} + +call_init!(init); +fn init(our: Address) { + print_to_terminal(0, "begin"); + + loop { + match handle_message(&our) { + Ok(()) => {}, + Err(e) => { + print_to_terminal(0, format!("{package_name}_test: error: {e:?}").as_str()); + + fail!("{package_name}_test"); + }, + }; + } +} diff --git a/src/new/templates/test/echo/{package_name}_test/{package_name}_test/src/tester_lib.rs b/src/new/templates/test/echo/{package_name}_test/{package_name}_test/src/tester_lib.rs new file mode 100644 index 00000000..9b367d36 --- /dev/null +++ b/src/new/templates/test/echo/{package_name}_test/{package_name}_test/src/tester_lib.rs @@ -0,0 +1,31 @@ +use crate::kinode::process::tester::{ + Response as TesterResponse, FailResponse, +}; + +#[macro_export] +macro_rules! fail { + ($test:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: file!().into(), + line: line!(), + column: column!(), + }))) + .send() + .unwrap(); + panic!("") + }; + ($test:expr, $file:expr, $line:expr, $column:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: $file.into(), + line: $line, + column: $column, + }))) + .send() + .unwrap(); + panic!("") + }; +} diff --git a/src/new/templates/test/fibonacci/tests.toml b/src/new/templates/test/fibonacci/tests.toml new file mode 100644 index 00000000..e1172f01 --- /dev/null +++ b/src/new/templates/test/fibonacci/tests.toml @@ -0,0 +1,18 @@ +runtime = { FetchVersion = "latest" } +# runtime = { RepoPath = "~/git/kinode" } +runtime_build_release = false + + +[[tests]] +setup_package_paths = [".."] +test_packages = [ + { path = "{package_name}_test", grant_capabilities = ["{package_name}:{package_name}:{publisher}"] }, +] +timeout_secs = 5 +fakechain_router = 8545 + +[[tests.nodes]] +port = 8080 +home = "home/first" +fake_node_name = "first.dev" +runtime_verbosity = 0 diff --git a/src/new/templates/test/fibonacci/{package_name}_test/Cargo.toml_ b/src/new/templates/test/fibonacci/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..6c797c34 --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/Cargo.toml_ @@ -0,0 +1,10 @@ +[workspace] +resolver = "2" +members = [ + "{package_name}_test", +] + +[profile.release] +panic = "abort" +opt-level = "s" +lto = true diff --git a/src/new/templates/test/fibonacci/{package_name}_test/api/tester:sys-v0.wit b/src/new/templates/test/fibonacci/{package_name}_test/api/tester:sys-v0.wit new file mode 100644 index 00000000..7fe0574b --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/api/tester:sys-v0.wit @@ -0,0 +1,27 @@ +interface tester { + variant request { + run(run-request), + } + + variant response { + run(result<_, fail-response>) + } + + record run-request { + input-node-names: list, + test-names: list, + test-timeout: u64, + } + + record fail-response { + test: string, + file: string, + line: u32, + column: u32, + } +} + +world tester-sys-v0 { + import tester; + include process-v0; +} diff --git a/src/new/templates/test/fibonacci/{package_name}_test/api/{package_name}:{publisher}-v0.wit b/src/new/templates/test/fibonacci/{package_name}_test/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..73615f3f --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,16 @@ +interface {package_name_kebab} { + variant request { + number(u32), + numbers(tuple), + } + + variant response { + number(u64), + numbers(tuple), + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process-v0; +} diff --git a/src/new/templates/test/fibonacci/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit b/src/new/templates/test/fibonacci/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit new file mode 100644 index 00000000..79c71980 --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit @@ -0,0 +1,5 @@ +world {package_name_kebab}-test-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + import tester; + include process-v0; +} diff --git a/src/new/templates/test/fibonacci/{package_name}_test/metadata.json b/src/new/templates/test/fibonacci/{package_name}_test/metadata.json new file mode 100644 index 00000000..cea44761 --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/metadata.json @@ -0,0 +1,17 @@ +{ + "name": "{package_name} Test", + "description": "A test for {package_name}.", + "image": "", + "properties": { + "package_name": "{package_name}_test", + "current_version": "0.1.0", + "publisher": "{publisher}", + "mirrors": [], + "code_hashes": { + "0.1.0": "" + }, + "wit_version": 0 + }, + "external_url": "", + "animation_url": "" +} diff --git a/src/new/templates/test/fibonacci/{package_name}_test/pkg/manifest.json b/src/new/templates/test/fibonacci/{package_name}_test/pkg/manifest.json new file mode 100644 index 00000000..61ef3388 --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/pkg/manifest.json @@ -0,0 +1,13 @@ +[ + { + "process_name": "{package_name}_test", + "process_wasm_path": "/{package_name}_test.wasm", + "on_exit": "Restart", + "request_networking": false, + "request_capabilities": [], + "grant_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ], + "public": true + } +] diff --git a/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/Cargo.toml_ b/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..402919e0 --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/Cargo.toml_ @@ -0,0 +1,21 @@ +[package] +name = "{package_name}_test" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +bincode = "1.3" +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "b492f3b" } +process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" } +rmp-serde = "1.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/src/lib.rs b/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/src/lib.rs new file mode 100644 index 00000000..7e33be91 --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/src/lib.rs @@ -0,0 +1,104 @@ +use crate::kinode::process::{package_name}::{Request as FibRequest, Response as FibResponse}; +use crate::kinode::process::tester::{Request as TesterRequest, Response as TesterResponse, RunRequest, FailResponse}; + +use kinode_process_lib::{await_message, call_init, print_to_terminal, Address, ProcessId, Request, Response}; + +mod tester_lib; + +wit_bindgen::generate!({ + path: "target/wit", + world: "{package_name_kebab}-test-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], +}); + +fn test_number(n: u32, address: &Address) -> anyhow::Result { + let response = Request::new() + .target(address) + .body(FibRequest::Number(n)) + .send_and_await_response(15)?.unwrap(); + if response.is_request() { fail!("{package_name}_test"); }; + let FibResponse::Number(fib_number) = response.body().try_into()? else { + fail!("{package_name}_test"); + }; + Ok(fib_number) +} + +fn test_numbers(n: u32, n_trials: u32, address: &Address) -> anyhow::Result { + let response = Request::new() + .target(address) + .body(FibRequest::Numbers((n, n_trials))) + .send_and_await_response(15)?.unwrap(); + if response.is_request() { fail!("{package_name}_test"); }; + let FibResponse::Numbers((fib_number, _)) = response.body().try_into()? else { + fail!("{package_name}_test"); + }; + Ok(fib_number) +} + +fn handle_message (our: &Address) -> anyhow::Result<()> { + let message = await_message().unwrap(); + + if !message.is_request() { + unimplemented!(); + } + let source = message.source(); + if our.node != source.node { + return Err(anyhow::anyhow!( + "rejecting foreign Message from {:?}", + source, + )); + } + let TesterRequest::Run(RunRequest { + input_node_names: node_names, + .. + }) = message.body().try_into()?; + print_to_terminal(0, "{package_name}_test: a"); + assert!(node_names.len() == 1); + + let our_fib_address = Address { + node: our.node.clone(), + process: ProcessId::new(Some("{package_name}"), "{package_name}", "{publisher}"), + }; + + let numbers = vec![0, 1, 2, 5, 10, 20, 30, 47]; + let expecteds = vec![0, 1, 1, 5, 55, 6_765, 832_040, 2_971_215_073]; + for (number, expected) in numbers.iter().zip(expecteds.iter()) { + let result = test_number(number.clone(), &our_fib_address)?; + if &result != expected { + fail!("{package_name}_test"); + } + } + + let numbers = vec![0, 1, 2, 5, 10, 20, 30, 47]; + let expecteds = vec![0, 1, 1, 5, 55, 6_765, 832_040, 2_971_215_073]; + for (number, expected) in numbers.iter().zip(expecteds.iter()) { + let result = test_numbers(number.clone(), 5, &our_fib_address)?; + if &result != expected { + fail!("{package_name}_test"); + } + } + + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + + Ok(()) +} + +call_init!(init); +fn init(our: Address) { + print_to_terminal(0, "begin"); + + loop { + match handle_message(&our) { + Ok(()) => {}, + Err(e) => { + print_to_terminal(0, format!("{package_name}_test: error: {e:?}").as_str()); + + fail!("{package_name}_test"); + }, + }; + } +} diff --git a/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/src/tester_lib.rs b/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/src/tester_lib.rs new file mode 100644 index 00000000..9b367d36 --- /dev/null +++ b/src/new/templates/test/fibonacci/{package_name}_test/{package_name}_test/src/tester_lib.rs @@ -0,0 +1,31 @@ +use crate::kinode::process::tester::{ + Response as TesterResponse, FailResponse, +}; + +#[macro_export] +macro_rules! fail { + ($test:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: file!().into(), + line: line!(), + column: column!(), + }))) + .send() + .unwrap(); + panic!("") + }; + ($test:expr, $file:expr, $line:expr, $column:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: $file.into(), + line: $line, + column: $column, + }))) + .send() + .unwrap(); + panic!("") + }; +} diff --git a/src/new/templates/test/file_transfer/tests.toml b/src/new/templates/test/file_transfer/tests.toml new file mode 100644 index 00000000..4dbc9824 --- /dev/null +++ b/src/new/templates/test/file_transfer/tests.toml @@ -0,0 +1,24 @@ +runtime = { FetchVersion = "latest" } +# runtime = { RepoPath = "~/git/kinode" } +runtime_build_release = false + + +[[tests]] +setup_package_paths = [".."] +test_packages = [ + { path = "{package_name}_test", grant_capabilities = ["{package_name}:{package_name}:{publisher}"] }, +] +timeout_secs = 5 +fakechain_router = 8545 + +[[tests.nodes]] +port = 8080 +home = "home/first" +fake_node_name = "first.dev" +runtime_verbosity = 0 + +[[tests.nodes]] +port = 8081 +home = "home/second" +fake_node_name = "second.dev" +runtime_verbosity = 0 diff --git a/src/new/templates/test/file_transfer/{package_name}_test/Cargo.toml_ b/src/new/templates/test/file_transfer/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..6c797c34 --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/Cargo.toml_ @@ -0,0 +1,10 @@ +[workspace] +resolver = "2" +members = [ + "{package_name}_test", +] + +[profile.release] +panic = "abort" +opt-level = "s" +lto = true diff --git a/src/new/templates/test/file_transfer/{package_name}_test/api/tester:sys-v0.wit b/src/new/templates/test/file_transfer/{package_name}_test/api/tester:sys-v0.wit new file mode 100644 index 00000000..7fe0574b --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/api/tester:sys-v0.wit @@ -0,0 +1,27 @@ +interface tester { + variant request { + run(run-request), + } + + variant response { + run(result<_, fail-response>) + } + + record run-request { + input-node-names: list, + test-names: list, + test-timeout: u64, + } + + record fail-response { + test: string, + file: string, + line: u32, + column: u32, + } +} + +world tester-sys-v0 { + import tester; + include process-v0; +} diff --git a/src/new/templates/test/file_transfer/{package_name}_test/api/{package_name}:{publisher}-v0.wit b/src/new/templates/test/file_transfer/{package_name}_test/api/{package_name}:{publisher}-v0.wit new file mode 100644 index 00000000..3ae9a97c --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/api/{package_name}:{publisher}-v0.wit @@ -0,0 +1,53 @@ +interface {package_name_kebab} { + use standard.{address}; + + variant request { + list-files, + download(download-request), + progress(progress-request), + } + + variant response { + list-files(list), + download, + done, + started, + } + + variant worker-request { + initialize(initialize-request), + chunk(chunk-request), + size(u64), + } + + record download-request { + name: string, + target: address, + } + + record progress-request { + name: string, + progress: u64, + } + + record file-info { + name: string, + size: u64, + } + + record initialize-request { + name: string, + target-worker: option
, + } + + record chunk-request { + name: string, + offset: u64, + length: u64, + } +} + +world {package_name_kebab}-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + include process-v0; +} diff --git a/src/new/templates/test/file_transfer/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit b/src/new/templates/test/file_transfer/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit new file mode 100644 index 00000000..79c71980 --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/api/{package_name}_test:{publisher}-v0.wit @@ -0,0 +1,5 @@ +world {package_name_kebab}-test-{publisher_dotted_kebab}-v0 { + import {package_name_kebab}; + import tester; + include process-v0; +} diff --git a/src/new/templates/test/file_transfer/{package_name}_test/metadata.json b/src/new/templates/test/file_transfer/{package_name}_test/metadata.json new file mode 100644 index 00000000..cea44761 --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/metadata.json @@ -0,0 +1,17 @@ +{ + "name": "{package_name} Test", + "description": "A test for {package_name}.", + "image": "", + "properties": { + "package_name": "{package_name}_test", + "current_version": "0.1.0", + "publisher": "{publisher}", + "mirrors": [], + "code_hashes": { + "0.1.0": "" + }, + "wit_version": 0 + }, + "external_url": "", + "animation_url": "" +} diff --git a/src/new/templates/test/file_transfer/{package_name}_test/pkg/manifest.json b/src/new/templates/test/file_transfer/{package_name}_test/pkg/manifest.json new file mode 100644 index 00000000..61ef3388 --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/pkg/manifest.json @@ -0,0 +1,13 @@ +[ + { + "process_name": "{package_name}_test", + "process_wasm_path": "/{package_name}_test.wasm", + "on_exit": "Restart", + "request_networking": false, + "request_capabilities": [], + "grant_capabilities": [ + "{package_name}:{package_name}:{publisher}" + ], + "public": true + } +] diff --git a/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/Cargo.toml_ b/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/Cargo.toml_ new file mode 100644 index 00000000..402919e0 --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/Cargo.toml_ @@ -0,0 +1,21 @@ +[package] +name = "{package_name}_test" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +bincode = "1.3" +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "b492f3b" } +process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" } +rmp-serde = "1.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/src/lib.rs b/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/src/lib.rs new file mode 100644 index 00000000..c8672e78 --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/src/lib.rs @@ -0,0 +1,135 @@ +use crate::kinode::process::standard::{ProcessId as WitProcessId}; +use crate::kinode::process::{package_name}::{Address as WitAddress, Request as TransferRequest, Response as TransferResponse, WorkerRequest, DownloadRequest, ProgressRequest, FileInfo, InitializeRequest}; +use crate::kinode::process::tester::{Request as TesterRequest, Response as TesterResponse, RunRequest, FailResponse}; + +use kinode_process_lib::{await_message, call_init, print_to_terminal, println, Address, ProcessId, Request, Response}; + +mod tester_lib; + +wit_bindgen::generate!({ + path: "target/wit", + world: "{package_name_kebab}-test-{publisher_dotted_kebab}-v0", + generate_unused_types: true, + additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], +}); + +fn test_list_files(address: &Address) -> anyhow::Result> { + let response = Request::new() + .target(address) + .body(TransferRequest::ListFiles) + .send_and_await_response(15)?.unwrap(); + if response.is_request() { fail!("{package_name}_test"); }; + let TransferResponse::ListFiles(files) = response.body().try_into()? else { + fail!("{package_name}_test"); + }; + Ok(files) +} + +fn test_download(name: String, our: &Address, address: &Address) -> anyhow::Result<()> { + let response = Request::new() + .target(our) + .body(TransferRequest::Download(DownloadRequest { + name, + target: address, + })) + .send_and_await_response(15)?.unwrap(); + if response.is_request() { fail!("{package_name}_test"); }; + let TransferResponse::Download = response.body().try_into()? else { + fail!("{package_name}_test"); + }; + Ok(()) +} + +fn handle_message (our: &Address) -> anyhow::Result<()> { + let message = await_message().unwrap(); + + if !message.is_request() { + unimplemented!(); + } + let source = message.source(); + if our.node != source.node { + return Err(anyhow::anyhow!( + "rejecting foreign Message from {:?}", + source, + )); + } + let TesterRequest::Run(RunRequest { + input_node_names: node_names, + .. + }) = message.body().try_into()?; + print_to_terminal(0, "{package_name}_test: a"); + assert!(node_names.len() >= 2); + if our.node != node_names[0] { + // we are not master node: return + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + return Ok(()); + } + + // we are master node + + let our_ft_address = Address { + node: our.node.clone(), + process: ProcessId::new(Some("{package_name}"), "{package_name}", "{publisher}"), + }; + let their_ft_address = Address { + node: node_names[1].clone(), + process: ProcessId::new(Some("{package_name}"), "{package_name}", "{publisher}"), + }; + + // Send + print_to_terminal(0, "{package_name}_test: b"); + let message: String = "hello".into(); + let _ = Request::new() + .target(our_ft_address.clone()) + .body(ChatRequest::Send(SendRequest { + target: node_names[1].clone(), + message: message.clone(), + })) + .send_and_await_response(15)?.unwrap(); + + // Get history from receiver & test + print_to_terminal(0, "{package_name}_test: c"); + let response = Request::new() + .target(their_ft_address.clone()) + .body(ChatRequest::History(our.node.clone())) + .send_and_await_response(15)?.unwrap(); + if response.is_request() { fail!("{package_name}_test"); }; + let ChatResponse::History(messages) = response.body().try_into()? else { + fail!("{package_name}_test"); + }; + let expected_messages = vec![ChatMessage { + author: our.node.clone(), + content: message, + }]; + + if messages != expected_messages { + println!("{messages:?} != {expected_messages:?}"); + fail!("{package_name}_test"); + } + + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + + Ok(()) +} + +call_init!(init); +fn init(our: Address) { + print_to_terminal(0, "begin"); + + loop { + match handle_message(&our) { + Ok(()) => {}, + Err(e) => { + print_to_terminal(0, format!("{package_name}_test: error: {e:?}").as_str()); + + fail!("{package_name}_test"); + }, + }; + } +} diff --git a/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/src/tester_lib.rs b/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/src/tester_lib.rs new file mode 100644 index 00000000..9b367d36 --- /dev/null +++ b/src/new/templates/test/file_transfer/{package_name}_test/{package_name}_test/src/tester_lib.rs @@ -0,0 +1,31 @@ +use crate::kinode::process::tester::{ + Response as TesterResponse, FailResponse, +}; + +#[macro_export] +macro_rules! fail { + ($test:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: file!().into(), + line: line!(), + column: column!(), + }))) + .send() + .unwrap(); + panic!("") + }; + ($test:expr, $file:expr, $line:expr, $column:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: $file.into(), + line: $line, + column: $column, + }))) + .send() + .unwrap(); + panic!("") + }; +} diff --git a/src/run_tests/cleanup.rs b/src/run_tests/cleanup.rs index 416179ea..31c1037b 100644 --- a/src/run_tests/cleanup.rs +++ b/src/run_tests/cleanup.rs @@ -34,14 +34,22 @@ pub async fn cleanup_on_signal( send_to_cleanup: SendBool, mut recv_kill_in_cos: BroadcastRecvBool, ) { - let mut sigalrm = signal(SignalKind::alarm()).expect("kit run-tests: failed to set up SIGALRM handler"); - let mut sighup = signal(SignalKind::hangup()).expect("kit run-tests: failed to set up SIGHUP handler"); - let mut sigint = signal(SignalKind::interrupt()).expect("kit run-tests: failed to set up SIGINT handler"); - let mut sigpipe = signal(SignalKind::pipe()).expect("kit run-tests: failed to set up SIGPIPE handler"); - let mut sigquit = signal(SignalKind::quit()).expect("kit run-tests: failed to set up SIGQUIT handler"); - let mut sigterm = signal(SignalKind::terminate()).expect("kit run-tests: failed to set up SIGTERM handler"); - let mut sigusr1 = signal(SignalKind::user_defined1()).expect("kit run-tests: failed to set up SIGUSR1 handler"); - let mut sigusr2 = signal(SignalKind::user_defined2()).expect("kit run-tests: failed to set up SIGUSR2 handler"); + let mut sigalrm = signal(SignalKind::alarm()) + .expect("kit run-tests: failed to set up SIGALRM handler"); + let mut sighup = signal(SignalKind::hangup()) + .expect("kit run-tests: failed to set up SIGHUP handler"); + let mut sigint = signal(SignalKind::interrupt()) + .expect("kit run-tests: failed to set up SIGINT handler"); + let mut sigpipe = signal(SignalKind::pipe()) + .expect("kit run-tests: failed to set up SIGPIPE handler"); + let mut sigquit = signal(SignalKind::quit()) + .expect("kit run-tests: failed to set up SIGQUIT handler"); + let mut sigterm = signal(SignalKind::terminate()) + .expect("kit run-tests: failed to set up SIGTERM handler"); + let mut sigusr1 = signal(SignalKind::user_defined1()) + .expect("kit run-tests: failed to set up SIGUSR1 handler"); + let mut sigusr2 = signal(SignalKind::user_defined2()) + .expect("kit run-tests: failed to set up SIGUSR2 handler"); tokio::select! { _ = sigalrm.recv() => error!("kit cleanup got SIGALRM\r"), @@ -76,11 +84,16 @@ pub async fn cleanup( Some(nh) => { let mut nh = nh.lock().await; let nh_vec = std::mem::replace(&mut *nh, Vec::new()); - Some(nh_vec.into_iter()) + Some(nh_vec.into_iter().rev()) }, }; - for NodeCleanupInfo { master_fd, process_id, home, anvil_process } in node_cleanup_infos.iter_mut() { + for NodeCleanupInfo { + master_fd, + process_id, + home, + anvil_process, + } in node_cleanup_infos.iter_mut().rev() { // Send Ctrl-C to the process info!("Cleaning up {:?}...\r", home); @@ -139,7 +152,6 @@ pub async fn drain_print_runtime( stderr_buffer.push('\n'); } Ok(should_print_std) = recv_kill.recv() => { - println!("len stdout, stderr: {} {}", stdout_buffer.len(), stderr_buffer.len()); if should_print_std { let stdout = remove_repeated_newlines(&stdout_buffer); let stderr = remove_repeated_newlines(&stderr_buffer); diff --git a/src/run_tests/mod.rs b/src/run_tests/mod.rs index 2ba0d3e1..ebe435f6 100644 --- a/src/run_tests/mod.rs +++ b/src/run_tests/mod.rs @@ -14,12 +14,12 @@ use crate::chain; use crate::inject_message; use crate::start_package; +use crate::kinode::process::tester::{Response as TesterResponse, FailResponse}; + pub mod cleanup; use cleanup::{cleanup, cleanup_on_signal, drain_print_runtime}; pub mod types; use types::*; -mod tester_types; -use tester_types as tt; fn get_basename(file_path: &Path) -> Option<&str> { file_path @@ -272,14 +272,12 @@ async fn run_tests( match inject_message::parse_response(response).await { Ok(inject_message::Response { ref body, .. }) => { - match serde_json::from_str(body)? { - tt::TesterResponse::Pass => info!("PASS"), - tt::TesterResponse::Fail { test, file, line, column } => { + let TesterResponse::Run(result) = serde_json::from_str(body)?; + match result { + Ok(()) => info!("PASS"), + Err(FailResponse { test, file, line, column }) => { return Err(eyre!("FAIL: {} {}:{}:{}", test, file, line, column)); - }, - tt::TesterResponse::GetFullMessage(_) => { - return Err(eyre!("FAIL: Unexpected Response")); - }, + } } }, Err(e) => { @@ -291,12 +289,35 @@ async fn run_tests( } #[instrument(level = "trace", skip_all)] -async fn handle_test(detached: bool, runtime_path: &Path, test: Test) -> Result<()> { - for setup_package_path in &test.setup_package_paths { - build::execute(&setup_package_path, false, false, false, "", false).await?; +async fn handle_test( + detached: bool, + runtime_path: &Path, + test: Test, + test_dir_path: &Path, +) -> Result<()> { + let setup_package_paths: Vec = test.setup_package_paths + .iter() + .cloned() + .map(|p| test_dir_path.join(p).canonicalize().unwrap()) + .collect(); + let test_packages: Vec = test.test_packages + .iter() + .cloned() + .map(|tp| { + TestPackage { + path: test_dir_path.join(tp.path).canonicalize().unwrap(), + grant_capabilities: tp.grant_capabilities, + } + }) + .collect(); + //for setup_package_path in &test.setup_package_paths { + // let path = test_dir_path.join(setup_package_path).canonicalize()?; + for path in &setup_package_paths { + build::execute(&path, false, false, false, "", None, None, false).await?; // TODO } - for TestPackage { ref path, .. } in &test.test_packages { - build::execute(path, false, false, false, "", false).await?; + for TestPackage { ref path, .. } in &test_packages { + let path = test_dir_path.join(path).canonicalize()?; + build::execute(&path, false, false, false, "", None, None, false).await?; // TODO } // Initialize variables for master node and nodes list @@ -409,10 +430,10 @@ async fn handle_test(detached: bool, runtime_path: &Path, test: Test) -> Result< } for node in &test.nodes { - load_setups(&test.setup_package_paths, node.port.clone()).await?; + load_setups(&setup_package_paths, node.port.clone()).await?; } - load_tests(&test.test_packages, master_node_port.unwrap().clone()).await?; + load_tests(&test_packages, master_node_port.unwrap().clone()).await?; let ports = test.nodes.iter().map(|n| n.port).collect(); @@ -436,8 +457,26 @@ async fn handle_test(detached: bool, runtime_path: &Path, test: Test) -> Result< pub async fn execute(config_path: &str) -> Result<()> { let detached = true; // TODO: to arg? - let config_content = fs::read_to_string(config_path)?; - let config = toml::from_str::(&config_content)?.expand_home_paths(); + let config_path = PathBuf::from(config_path); + if !config_path.exists() { + return Err(eyre!("given config path {config_path:?} does not exist")); + } + let config_path = if config_path.is_file() { + config_path + } else { + let config_path = config_path.join("test").join("tests.toml"); + if !config_path.exists() { + return Err(eyre!("given config path {config_path:?} does not exist")); + } + if config_path.is_file() { + config_path + } else { + return Err(eyre!("given config path {config_path:?} is not a file")); + } + }; + + let content = fs::read_to_string(&config_path)?; + let config = toml::from_str::(&content)?.expand_home_paths(); debug!("{:?}", config); @@ -467,8 +506,10 @@ pub async fn execute(config_path: &str) -> Result<()> { }, }; + let test_dir_path = PathBuf::from(config_path).canonicalize()?; + let test_dir_path = test_dir_path.parent().unwrap(); for test in config.tests { - handle_test(detached, &runtime_path, test).await?; + handle_test(detached, &runtime_path, test, &test_dir_path).await?; } Ok(()) diff --git a/src/run_tests/tester_types.rs b/src/run_tests/tester_types.rs deleted file mode 100644 index 73c8c3c7..00000000 --- a/src/run_tests/tester_types.rs +++ /dev/null @@ -1,91 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use kinode_process_lib::kernel_types as kt; -use kinode_process_lib::Address; - -type Rsvp = Option
; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KernelMessage { - pub id: u64, - pub source: Address, - pub target: Address, - pub rsvp: Rsvp, - pub message: kt::Message, - pub lazy_load_blob: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum TesterRequest { - Run { - input_node_names: Vec, - test_names: Vec, - test_timeout: u64, - }, - KernelMessage(KernelMessage), - GetFullMessage(kt::Message), -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TesterFail { - pub test: String, - pub file: String, - pub line: u32, - pub column: u32, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum TesterResponse { - Pass, - Fail { - test: String, - file: String, - line: u32, - column: u32, - }, - GetFullMessage(Option), -} - -#[derive(Debug, Serialize, Deserialize, thiserror::Error)] -pub enum TesterError { - #[error("RejectForeign")] - RejectForeign, - #[error("UnexpectedResponse")] - UnexpectedResponse, - #[error("FAIL {test} {message}")] - Fail { test: String, message: String }, -} - -#[macro_export] -macro_rules! fail { - ($test:expr) => { - Response::new() - .body( - serde_json::to_vec(&tt::TesterResponse::Fail { - test: $test.into(), - file: file!().into(), - line: line!(), - column: column!(), - }) - .unwrap(), - ) - .send() - .unwrap(); - panic!("") - }; - ($test:expr, $file:expr, $line:expr, $column:expr) => { - Response::new() - .body( - serde_json::to_vec(&tt::TesterResponse::Fail { - test: $test.into(), - file: $file.into(), - line: $line, - column: $column, - }) - .unwrap(), - ) - .send() - .unwrap(); - panic!("") - }; -} diff --git a/src/run_tests/wit/kinode.wit b/src/run_tests/wit/kinode.wit new file mode 100644 index 00000000..d7475411 --- /dev/null +++ b/src/run_tests/wit/kinode.wit @@ -0,0 +1,201 @@ +package kinode:process@0.8.0; + +interface standard { + // + // System types: + // + + // JSON is passed over WASM boundary as a string. + type json = string; + + type node-id = string; + + // Context, like a message body, is a protocol-defined serialized byte + // array. It is used when building a Request to save information that + // will not be part of a Response, in order to more easily handle + // ("contextualize") that Response. + type context = list; + + record process-id { + process-name: string, + package-name: string, + publisher-node: node-id, + } + + record package-id { + package-name: string, + publisher-node: node-id, + } + + record address { + node: node-id, + process: process-id, + } + + record lazy-load-blob { + mime: option, + bytes: list, + } + + record request { + // set in order to inherit lazy-load-blob from parent message, and if + // expects-response is none, direct response to source of parent. + // also carries forward certain aspects of parent message in kernel, + // see documentation for formal spec and examples. + inherit: bool, + // if some, request expects a response in the given number of seconds + expects-response: option, + body: list, + metadata: option, + capabilities: list, + // to grab lazy-load-blob, use get_blob() + } + + record response { + inherit: bool, + body: list, + metadata: option, + capabilities: list, + // to grab lazy-load-blob, use get_blob() + } + + // A message can be a request or a response. within a response, there is + // a result which surfaces any error that happened because of a request. + // A successful response will contain the context of the request it + // matches, if any was set. + variant message { + request(request), + response(tuple>), + } + + record capability { + issuer: address, + params: json, + } + + // On-exit is a setting that determines what happens when a process + // panics, completes, or otherwise "ends". NOTE: requests should have + // expects-response set to false, will always be set to that by kernel. + variant on-exit { + none, + restart, + requests(list>>), + } + + // Network errors come from trying to send a message to another node. + // A message can fail by timing out, or by the node being entirely + // unreachable (offline). In either case, the message is not delivered + // and the process that sent it receives that message along with any + // assigned context and/or lazy-load-blob, and is free to handle it as it + // sees fit. + record send-error { + kind: send-error-kind, + target: address, + message: message, + lazy-load-blob: option, + } + + enum send-error-kind { + offline, + timeout, + } + + enum spawn-error { + name-taken, + no-file-at-path, + } + + // + // System utils: + // + + print-to-terminal: func(verbosity: u8, message: string); + + // + // Process management: + // + + set-on-exit: func(on-exit: on-exit); + + get-on-exit: func() -> on-exit; + + get-state: func() -> option>; + + set-state: func(bytes: list); + + clear-state: func(); + + spawn: func( + name: option, + wasm-path: string, // must be located within package's drive + on-exit: on-exit, + request-capabilities: list, + // note that we are restricting granting to just messaging the + // newly spawned process + grant-capabilities: list, + public: bool + ) -> result; + + // + // Capabilities management: + // + + // Saves the capabilities to persisted process state. + save-capabilities: func(caps: list); + + // Deletes the capabilities from persisted process state. + drop-capabilities: func(caps: list); + + // Gets all capabilities from persisted process state. + our-capabilities: func() -> list; + + // + // Message I/O: + // + + // Ingest next message when it arrives along with its source. + // Almost all long-running processes will call this in a loop. + receive: func() -> + result, tuple>>; + + // Gets lazy-load-blob, if any, of the message we most recently received. + get-blob: func() -> option; + + // Send message(s) to target(s). + send-request: func( + target: address, + request: request, + context: option, + lazy-load-blob: option + ); + + send-requests: func( + requests: list, + option>> + ); + + send-response: func( + response: response, + lazy-load-blob: option + ); + + // Send a single request, then block (internally) until its response. The + // type returned is Message but will always contain Response. + send-and-await-response: func( + target: address, + request: request, + lazy-load-blob: option + ) -> result, send-error>; +} + +world lib { + import standard; +} + +world process-v0 { + include lib; + + export init: func(our: string); +} diff --git a/src/run_tests/wit/tester:sys-v0.wit b/src/run_tests/wit/tester:sys-v0.wit new file mode 100644 index 00000000..7fe0574b --- /dev/null +++ b/src/run_tests/wit/tester:sys-v0.wit @@ -0,0 +1,27 @@ +interface tester { + variant request { + run(run-request), + } + + variant response { + run(result<_, fail-response>) + } + + record run-request { + input-node-names: list, + test-names: list, + test-timeout: u64, + } + + record fail-response { + test: string, + file: string, + line: u32, + column: u32, + } +} + +world tester-sys-v0 { + import tester; + include process-v0; +} diff --git a/src/start_package/mod.rs b/src/start_package/mod.rs index 7250b632..75271899 100644 --- a/src/start_package/mod.rs +++ b/src/start_package/mod.rs @@ -11,18 +11,37 @@ use zip::write::FileOptions; use kinode_process_lib::kernel_types::{Erc721Metadata, PackageManifestEntry}; -use crate::{inject_message, KIT_LOG_PATH_DEFAULT}; +use crate::{build::read_metadata, inject_message, KIT_LOG_PATH_DEFAULT}; #[instrument(level = "trace", skip_all)] fn new_package( node: Option<&str>, package_name: &str, publisher_node: &str, + metadata: &Erc721Metadata, bytes_path: &str, ) -> Result { let message = json!({ "NewPackage": { - "package": {"package_name": package_name, "publisher_node": publisher_node}, + "package_id": {"package_name": package_name, "publisher_node": publisher_node}, + "metadata": { + "name": metadata.name, + "description": metadata.description, + "image": metadata.image, + "external_url": metadata.external_url, + "animation_url": metadata.animation_url, + "properties": { + "package_name": metadata.properties.package_name, + "publisher": metadata.properties.publisher, + "current_version": metadata.properties.current_version, + "mirrors": metadata.properties.mirrors, + "code_hashes": metadata.properties.code_hashes.clone().into_iter().collect::>(), + "license": metadata.properties.license, + "screenshots": metadata.properties.screenshots, + "wit_version": metadata.properties.wit_version, + "dependencies": metadata.properties.dependencies, + }, + }, "mirror": true } }); @@ -62,7 +81,7 @@ pub fn interact_with_package( } #[instrument(level = "trace", skip_all)] -fn zip_directory(directory: &Path, zip_filename: &str) -> Result<()> { +pub fn zip_directory(directory: &Path, zip_filename: &str) -> Result<()> { let file = fs::File::create(zip_filename)?; let walkdir = WalkDir::new(directory); let it = walkdir.into_iter(); @@ -104,10 +123,7 @@ pub async fn execute(package_dir: &Path, url: &str) -> Result<()> { )); } let pkg_dir = package_dir.join("pkg").canonicalize()?; - let metadata: Erc721Metadata = - serde_json::from_reader(fs::File::open(package_dir.join("metadata.json")) - .wrap_err_with(|| "Missing required metadata.json file. See discussion at https://book.kinode.org/my_first_app/chapter_1.html?highlight=metadata.json#metadatajson")? - )?; + let metadata = read_metadata(package_dir)?; let package_name = metadata.properties.package_name.as_str(); let publisher = metadata.properties.publisher.as_str(); let pkg_publisher = format!("{}:{}", package_name, publisher); @@ -150,6 +166,7 @@ pub async fn execute(package_dir: &Path, url: &str) -> Result<()> { None, package_name, publisher, + &metadata, zip_filename.to_str().unwrap(), )?; let response = inject_message::send_request(url, new_pkg_request).await?; diff --git a/src/view_api/mod.rs b/src/view_api/mod.rs new file mode 100644 index 00000000..6e7689f2 --- /dev/null +++ b/src/view_api/mod.rs @@ -0,0 +1,117 @@ +use std::path::PathBuf; + +use color_eyre::{Result, eyre::eyre}; +use fs_err as fs; +use serde_json::json; +use tracing::{info, instrument}; + +use crate::{boot_fake_node::extract_zip, inject_message, KIT_CACHE, KIT_LOG_PATH_DEFAULT}; + +#[instrument(level = "trace", skip_all)] +fn list_apis(node: Option<&str>) -> Result { + let message = json!("ListApis"); + + inject_message::make_message( + "main:app_store:sys", + Some(5), + &message.to_string(), + node, + None, + None, + ) +} + +#[instrument(level = "trace", skip_all)] +fn get_api( + node: Option<&str>, + package_name: &str, + publisher_node: &str, +) -> Result { + let message = json!({ + "GetApi": { + "package_name": package_name, + "publisher_node": publisher_node, + }, + }); + + inject_message::make_message( + "main:app_store:sys", + Some(5), + &message.to_string(), + node, + None, + None, + ) +} + +#[instrument(level = "trace", skip_all)] +fn split_package_id(package_id: &str) -> Result<(String, String)> { + let mut pids = package_id.splitn(2, ':'); + let (Some(package_name), Some(publisher_node), None) = ( + pids.next(), + pids.next(), + pids.next(), + ) else { + return Err(eyre!("package_id must be None or Some(:)")); + }; + Ok((package_name.to_string(), publisher_node.to_string())) +} + +#[instrument(level = "trace", skip_all)] +pub async fn execute( + node: Option<&str>, + package_id: Option<&str>, + url: &str, + verbose: bool, +) -> Result> { + let request = if let Some(package_id) = package_id { + let (package_name, publisher_node) = split_package_id(package_id)?; + get_api(node, &package_name, &publisher_node)? + } else { + list_apis(node)? + }; + let response = inject_message::send_request(url, request).await?; + + let inject_message::Response { ref body, ref lazy_load_blob, .. } = + inject_message::parse_response(response) + .await + .map_err(|e| { + let e_string = e.to_string(); + if e_string.contains("Failed with status code:") { + eyre!("{}\ncheck logs (default at {}) for full http response\n\nhint: is Kinode running at url {}?", e_string, KIT_LOG_PATH_DEFAULT, url) + } else { + eyre!(e_string) + } + })?; + let body = serde_json::from_str::(body)?; + let zip_dir = if let Some(blob) = lazy_load_blob { + let api_name = format!("{}-api", package_id.unwrap()); + let zip_dir = PathBuf::from(KIT_CACHE).join(api_name); + let zip_path = zip_dir.join(format!("{}-api.zip", package_id.unwrap())); + if zip_dir.exists() { + fs::remove_dir_all(&zip_dir)?; + } + fs::create_dir_all(&zip_dir)?; + fs::write(&zip_path, blob)?; + extract_zip(&zip_path)?; + for entry in zip_dir.read_dir()? { + let entry = entry?; + let path = entry.path(); + if Some("wit") == path.extension().and_then(|s| s.to_str()) { + let file_path = path.to_str().unwrap_or_default(); + let wit_contents = fs::read_to_string(&path)?; + if verbose { + info!("{}\n\n{}", file_path, wit_contents); + } + } + } + Some(zip_dir) + } else { + if verbose { + info!("{}", serde_json::to_string_pretty(&body)?); + } + None + }; + + Ok(zip_dir) +}