diff --git a/hyperdrive/packages/spider/crates/hyperware-parse-wit/.gitignore b/hyperdrive/packages/spider/crates/hyperware-parse-wit/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/hyperdrive/packages/spider/crates/hyperware-parse-wit/.gitignore @@ -0,0 +1 @@ +/target diff --git a/hyperdrive/packages/spider/crates/hyperware-parse-wit/Cargo.lock b/hyperdrive/packages/spider/crates/hyperware-parse-wit/Cargo.lock new file mode 100644 index 000000000..eaa74afe3 --- /dev/null +++ b/hyperdrive/packages/spider/crates/hyperware-parse-wit/Cargo.lock @@ -0,0 +1,809 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bzip2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" +dependencies = [ + "bzip2-sys", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deflate64" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" + +[[package]] +name = "deranged" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "hyperware-parse-wit" +version = "0.1.0" +dependencies = [ + "anyhow", + "serde_json", + "wit-parser", + "zip", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.225" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.225" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.225" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmparser" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d07b6a3b550fefa1a914b6d54fc175dd11c3392da11eee604e6ffc759805d25" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "wit-parser" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a7999ed18efe59be8de2db9cb2b7f84d88b27818c79353dfc53131840fe1a" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "getrandom", + "hmac", + "indexmap", + "lzma-rs", + "memchr", + "pbkdf2", + "sha1", + "thiserror", + "time", + "xz2", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zopfli" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/hyperdrive/packages/spider/crates/hyperware-parse-wit/Cargo.toml b/hyperdrive/packages/spider/crates/hyperware-parse-wit/Cargo.toml new file mode 100644 index 000000000..3a52e2d2d --- /dev/null +++ b/hyperdrive/packages/spider/crates/hyperware-parse-wit/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "hyperware-parse-wit" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +serde_json = "1.0" +wit-parser = { version = "0.220.0", features = ["serde"] } +zip = { version = "2.2", default-features = false, features = ["deflate"] } diff --git a/hyperdrive/packages/spider/crates/hyperware-parse-wit/examples/parse_zip.rs b/hyperdrive/packages/spider/crates/hyperware-parse-wit/examples/parse_zip.rs new file mode 100644 index 000000000..ef26a2eb5 --- /dev/null +++ b/hyperdrive/packages/spider/crates/hyperware-parse-wit/examples/parse_zip.rs @@ -0,0 +1,38 @@ +use anyhow::Result; +use hyperware_parse_wit::parse_wit_from_zip; +use std::env; +use std::fs; + +fn main() -> Result<()> { + // Get the zip file path from command line arguments + let args: Vec = env::args().collect(); + if args.len() < 2 || args.len() > 3 { + eprintln!( + "Usage: {} [path-to-fallback-wit]", + args[0] + ); + eprintln!(" If no fallback WIT is provided, uses built-in hyperware.wit"); + std::process::exit(1); + } + + let zip_path = &args[1]; + + // Read the zip file into memory + let zip_bytes = fs::read(zip_path).expect(&format!("Failed to read zip file: {}", zip_path)); + + // Optionally read custom fallback WIT + let fallback_wit = if args.len() == 3 { + Some(fs::read(&args[2]).expect(&format!("Failed to read fallback WIT file: {}", args[2]))) + } else { + None + }; + + // Parse WIT from the zip and get JSON + // If no package header is found in the zip, it will use the fallback WIT + let json = parse_wit_from_zip(&zip_bytes, fallback_wit)?; + + // Print the JSON output + println!("{}", json); + + Ok(()) +} diff --git a/hyperdrive/packages/spider/crates/hyperware-parse-wit/src/hyperware.wit b/hyperdrive/packages/spider/crates/hyperware-parse-wit/src/hyperware.wit new file mode 100644 index 000000000..db96383b5 --- /dev/null +++ b/hyperdrive/packages/spider/crates/hyperware-parse-wit/src/hyperware.wit @@ -0,0 +1,224 @@ +package hyperware:process@1.0.0; + +interface standard { + + // ˗ˏˋ ♡ ˎˊ˗ + // System Types + // ˗ˏˋ ♡ ˎˊ˗ + + /// JSON is passed over Wasm boundary as a string. + type json = string; + + /// In types passed from kernel, node-id will be a valid Kimap entry. + 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: + // https://docs.rs/hyperware_process_lib/latest/hyperware_process_lib/struct.Request.html + 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 will always have expects-response set to false by kernel. + variant on-exit { + none, + restart, + requests(list>>), + } + + /// Send errors come from trying to send a message to another process, + /// either locally or on another node. + /// A message can fail by timing out, or by the node being entirely + /// unreachable (offline or can't be found in PKI). In either case, + /// the message is not delivered and the process that sent it receives + /// that message back along with any assigned context and/or lazy-load-blob, + /// and is free to handle it as it sees fit. + /// In the local case, only timeout errors are possible and also cover the case + /// in which a process is not running or does not exist. + 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 + // ˗ˏˋ ♡ ˎˊ˗ + + /// Prints to the terminal at a given verbosity level. + /// Higher verbosity levels print more information. + /// Level 0 is always printed -- use sparingly. + print-to-terminal: func(verbosity: u8, message: string); + + /// Returns the address of the process. + our: func() -> address; + + // ˗ˏˋ ♡ ˎˊ˗ + // Process Management + // ˗ˏˋ ♡ ˎˊ˗ + + get-on-exit: func() -> on-exit; + + set-on-exit: func(on-exit: on-exit); + + get-state: func() -> option>; + + set-state: func(bytes: list); + + clear-state: func(); + + spawn: func( + // name is optional. if not provided, name will be a random u64. + name: option, + // wasm-path must be located within package's drive + wasm-path: string, + on-exit: on-exit, + // requested capabilities must be owned by the caller + request-capabilities: list, + // granted capabilities will be generated by the child process + // and handed out to the indicated process-id. + 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>>; + + /// Returns whether or not the current message has a blob. + has-blob: func() -> bool; + + /// Returns the blob of the current message, if any. + get-blob: func() -> option; + + /// Returns the last blob this process received. + last-blob: func() -> option; + + /// Send request to target. + send-request: func( + target: address, + request: request, + context: option, + lazy-load-blob: option + ); + + /// Send requests to targets. + send-requests: func( + requests: list, + option>> + ); + + /// Send response to the request currently being handled. + 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-v1 { + include lib; + + export init: func(our: string); +} diff --git a/hyperdrive/packages/spider/crates/hyperware-parse-wit/src/lib.rs b/hyperdrive/packages/spider/crates/hyperware-parse-wit/src/lib.rs new file mode 100644 index 000000000..84409dfee --- /dev/null +++ b/hyperdrive/packages/spider/crates/hyperware-parse-wit/src/lib.rs @@ -0,0 +1,116 @@ +use anyhow::{Context, Result}; +use std::io::Read; +use std::path::Path; +use wit_parser::Resolve; +use zip::ZipArchive; + +// Include the default hyperware.wit file +const DEFAULT_HYPERWARE_WIT: &str = include_str!("hyperware.wit"); + +/// Parse WIT files from a zip archive and return JSON representation +/// +/// # Arguments +/// * `zip_bytes` - The bytes of the zip file containing WIT files +/// * `fallback_wit` - Optional WIT content to use when package header is missing. +/// If None, uses the built-in hyperware.wit +pub fn parse_wit_from_zip(zip_bytes: &[u8], fallback_wit: Option>) -> Result { + let resolve = parse_wit_from_zip_to_resolve(zip_bytes, fallback_wit)?; + let json = serde_json::to_string_pretty(&resolve).context("failed to serialize to JSON")?; + Ok(json) +} + +/// Parse WIT files from a zip archive and return parsed Resolve +/// +/// # Arguments +/// * `zip_bytes` - The bytes of the zip file containing WIT files +/// * `fallback_wit` - Optional WIT content to use when package header is missing. +/// If None, uses the built-in hyperware.wit +pub fn parse_wit_from_zip_to_resolve( + zip_bytes: &[u8], + fallback_wit: Option>, +) -> Result { + // Open the zip archive from bytes + let cursor = std::io::Cursor::new(zip_bytes); + let mut archive = ZipArchive::new(cursor).context("failed to open zip archive")?; + + let mut resolve = Resolve::default(); + let mut has_package_header = false; + let mut wit_files = Vec::new(); + + // First pass: collect all WIT files and check for package headers + for i in 0..archive.len() { + let mut file = archive.by_index(i).context("failed to access zip entry")?; + let name = file.name().to_string(); + + // Skip directories + if name.ends_with('/') { + continue; + } + + // Only process .wit files + if !name.ends_with(".wit") { + continue; + } + + // Read the file contents + let mut contents = String::new(); + file.read_to_string(&mut contents) + .with_context(|| format!("failed to read file: {}", name))?; + + // Check if this file contains a package header + if contents.contains("package ") && contents.contains("@") { + has_package_header = true; + } + + wit_files.push((name, contents)); + } + + // If no package header found, combine all WIT files with the fallback package header + if !has_package_header && !wit_files.is_empty() { + let fallback_content = match fallback_wit { + Some(bytes) => { + String::from_utf8(bytes).context("fallback WIT content is not valid UTF-8")? + } + None => DEFAULT_HYPERWARE_WIT.to_string(), + }; + + // Combine all WIT files into a single document with the package header + let mut combined_wit = fallback_content; + combined_wit.push_str("\n\n"); + + for (_, contents) in &wit_files { + combined_wit.push_str(&contents); + combined_wit.push_str("\n\n"); + } + + // Parse the combined WIT document + resolve + .push_str(Path::new("combined.wit"), &combined_wit) + .context("failed to parse combined WIT package")?; + } else { + // Parse each file individually if package headers are present + for (name, contents) in wit_files { + let path = Path::new(&name); + resolve + .push_str(path, &contents) + .with_context(|| format!("failed to parse WIT file: {}", name))?; + } + } + + Ok(resolve) +} + +/// Parse WIT files from a zip archive and return serde_json::Value +/// +/// # Arguments +/// * `zip_bytes` - The bytes of the zip file containing WIT files +/// * `fallback_wit` - Optional WIT content to use when package header is missing. +/// If None, uses the built-in hyperware.wit +pub fn parse_wit_from_zip_to_value( + zip_bytes: &[u8], + fallback_wit: Option>, +) -> Result { + let resolve = parse_wit_from_zip_to_resolve(zip_bytes, fallback_wit)?; + let value = serde_json::to_value(&resolve).context("failed to convert to JSON value")?; + Ok(value) +} diff --git a/hyperdrive/packages/spider/pkg/manifest.json b/hyperdrive/packages/spider/pkg/manifest.json index 61427bad5..3fb3d4fa8 100644 --- a/hyperdrive/packages/spider/pkg/manifest.json +++ b/hyperdrive/packages/spider/pkg/manifest.json @@ -5,6 +5,7 @@ "on_exit": "Restart", "request_networking": true, "request_capabilities": [ + "main:app-store:sys", "homepage:homepage:sys", "http-client:distro:sys", "http-server:distro:sys", @@ -14,6 +15,7 @@ "vfs:distro:sys" ], "grant_capabilities": [ + "main:app-store:sys", "homepage:homepage:sys", "http-client:distro:sys", "http-server:distro:sys", diff --git a/hyperdrive/packages/spider/spider/Cargo.toml b/hyperdrive/packages/spider/spider/Cargo.toml index d97888cf2..5a6107154 100644 --- a/hyperdrive/packages/spider/spider/Cargo.toml +++ b/hyperdrive/packages/spider/spider/Cargo.toml @@ -21,6 +21,9 @@ rev = "66884c0" git = "https://github.com/hyperware-ai/hyperware-anthropic-sdk" rev = "c0cbd5e" +[dependencies.hyperware-parse-wit] +path = "../crates/hyperware-parse-wit" + [dependencies.hyperware_process_lib] features = ["hyperapp"] git = "https://github.com/hyperware-ai/process_lib" @@ -37,6 +40,10 @@ features = [ ] version = "1.4.1" +[dependencies.wit-parser] +features = ["serde"] +version = "0.220.0" + [features] simulation-mode = [] diff --git a/hyperdrive/packages/spider/spider/src/lib.rs b/hyperdrive/packages/spider/spider/src/lib.rs index 2933ac2e5..fe984a202 100644 --- a/hyperdrive/packages/spider/spider/src/lib.rs +++ b/hyperdrive/packages/spider/spider/src/lib.rs @@ -48,6 +48,7 @@ mod tool_providers; use tool_providers::{ build_container::{BuildContainerExt, BuildContainerToolProvider}, hypergrid::{HypergridExt, HypergridToolProvider}, + hyperware::HyperwareToolProvider, ToolProvider, }; @@ -229,6 +230,53 @@ impl SpiderState { self.tool_provider_registry .register(Box::new(hypergrid_provider)); + // Register Hyperware tool provider + let hyperware_provider = HyperwareToolProvider::new(); + self.tool_provider_registry + .register(Box::new(hyperware_provider)); + + // Check if hyperware server exists + let has_hyperware = self + .mcp_servers + .iter() + .any(|s| s.transport.transport_type == "hyperware"); + if !has_hyperware { + // Create new hyperware server + let hyperware_provider = HyperwareToolProvider::new(); + let hyperware_tools = hyperware_provider.get_tools(self); + let hyperware_server = McpServer { + id: "hyperware".to_string(), + name: "Hyperware".to_string(), + transport: types::TransportConfig { + transport_type: "hyperware".to_string(), + command: None, + args: None, + url: None, + hypergrid_token: None, + hypergrid_client_id: None, + hypergrid_node: None, + }, + tools: hyperware_tools, + connected: true, // Always mark as connected + }; + self.mcp_servers.push(hyperware_server); + println!("Spider: Hyperware MCP server initialized"); + } else { + // Server exists, refresh its tools from the provider + println!("Spider: Refreshing Hyperware tools on startup"); + // Get fresh tools from provider + let hyperware_provider = HyperwareToolProvider::new(); + let fresh_tools = hyperware_provider.get_tools(self); + // Update the existing server's tools + if let Some(server) = self.mcp_servers.iter_mut().find(|s| s.id == "hyperware") { + server.tools = fresh_tools; + println!( + "Spider: Hyperware tools refreshed with {} tools", + server.tools.len() + ); + } + } + // Check if hypergrid server exists let has_hypergrid = self .mcp_servers @@ -2682,6 +2730,18 @@ impl SpiderState { Err(format!("Unknown build container tool: {}", tool_name)) } } + "hyperware" => { + // Native hyperware tools are handled by the tool provider + if let Some(provider) = self + .tool_provider_registry + .find_provider_for_tool(tool_name, self) + { + let command = provider.prepare_execution(tool_name, parameters, self)?; + self.execute_tool_command(command, conversation_id).await + } else { + Err(format!("Unknown hyperware tool: {}", tool_name)) + } + } "stdio" | "websocket" => { // Find the WebSocket connection for this server let channel_id = self @@ -3029,6 +3089,18 @@ impl SpiderState { self.execute_hypergrid_call_impl(server_id, provider_id, provider_name, call_args) .await } + ToolExecutionCommand::HyperwareSearchApis { query } => { + tool_providers::hyperware::search_apis(&query).await + } + ToolExecutionCommand::HyperwareGetApi { package_id } => { + tool_providers::hyperware::get_api(&package_id).await + } + ToolExecutionCommand::HyperwareCallApi { + process_id, + method, + args, + timeout, + } => tool_providers::hyperware::call_api(&process_id, &method, &args, timeout).await, ToolExecutionCommand::DirectResult(result) => result, } } diff --git a/hyperdrive/packages/spider/spider/src/tool_providers/hyperware.rs b/hyperdrive/packages/spider/spider/src/tool_providers/hyperware.rs new file mode 100644 index 000000000..80cbae0a5 --- /dev/null +++ b/hyperdrive/packages/spider/spider/src/tool_providers/hyperware.rs @@ -0,0 +1,576 @@ +use crate::tool_providers::{ToolExecutionCommand, ToolProvider}; +use crate::types::{SpiderState, Tool}; +use hyperware_parse_wit::parse_wit_from_zip_to_resolve; +use hyperware_process_lib::{get_blob, hyperapp::send, ProcessId, ProcessIdParseError, Request}; +use serde_json::{json, Value}; +use wit_parser::Docs; + +pub struct HyperwareToolProvider; + +impl HyperwareToolProvider { + pub fn new() -> Self { + Self + } + + fn create_search_apis_tool(&self) -> Tool { + Tool { + name: "hyperware_search_apis".to_string(), + description: "Search available APIs on Hyperware by querying the app store and filtering based on a search term.".to_string(), + parameters: r#"{"type":"object","required":["query"],"properties":{"query":{"type":"string","description":"Search term to filter available APIs (e.g., 'weather', 'database', 'auth')"}}}"#.to_string(), + input_schema_json: Some(r#"{"type":"object","required":["query"],"properties":{"query":{"type":"string","description":"Search term to filter available APIs (e.g., 'weather', 'database', 'auth')"}}}"#.to_string()), + } + } + + fn create_get_api_tool(&self) -> Tool { + Tool { + name: "hyperware_get_api".to_string(), + description: "Get the detailed API documentation for a specific package, including all available types and methods.".to_string(), + parameters: r#"{"type":"object","required":["package_id"],"properties":{"package_id":{"type":"string","description":"The package ID in the format 'package-name:publisher-node' (e.g., 'weather-app-9000:foo.os')"}}}"#.to_string(), + input_schema_json: Some(r#"{"type":"object","required":["package_id"],"properties":{"package_id":{"type":"string","description":"The package ID in the format 'package-name:publisher-node' (e.g., 'weather-app-9000:foo.os')"}}}"#.to_string()), + } + } + + fn create_call_api_tool(&self) -> Tool { + Tool { + name: "hyperware_call_api".to_string(), + description: "Call a specific API method on a Hyperware process to execute functionality.".to_string(), + parameters: r#"{"type":"object","required":["process_id","method","args"],"properties":{"process_id":{"type":"string","description":"The process ID in the format 'process-name:package-name:publisher-node'"},"method":{"type":"string","description":"The method name to call on the process. By convention UpperCamelCase"},"args":{"type":"string","description":"JSON string of arguments to pass to the method"},"timeout":{"type":"number","description":"Optional timeout in seconds (default: 15)"}}}"#.to_string(), + input_schema_json: Some(r#"{"type":"object","required":["process_id","method","args"],"properties":{"process_id":{"type":"string","description":"The process ID in the format 'process-name:package-name:publisher-node'"},"method":{"type":"string","description":"The method name to call on the process. By convention UpperCamelCase"},"args":{"type":"string","description":"JSON string of arguments to pass to the method"},"timeout":{"type":"number","description":"Optional timeout in seconds (default: 15)"}}}"#.to_string()), + } + } +} + +impl ToolProvider for HyperwareToolProvider { + fn get_tools(&self, _state: &SpiderState) -> Vec { + vec![ + self.create_search_apis_tool(), + self.create_get_api_tool(), + self.create_call_api_tool(), + ] + } + + fn should_include_tool(&self, tool_name: &str, _state: &SpiderState) -> bool { + match tool_name { + "hyperware_search_apis" | "hyperware_get_api" | "hyperware_call_api" => true, + _ => false, + } + } + + fn prepare_execution( + &self, + tool_name: &str, + parameters: &Value, + _state: &SpiderState, + ) -> Result { + match tool_name { + "hyperware_search_apis" => { + let query = parameters + .get("query") + .and_then(|v| v.as_str()) + .ok_or_else(|| "Missing query parameter".to_string())? + .to_string(); + + Ok(ToolExecutionCommand::HyperwareSearchApis { query }) + } + "hyperware_get_api" => { + let package_id = parameters + .get("package_id") + .and_then(|v| v.as_str()) + .ok_or_else(|| "Missing package_id parameter".to_string())? + .to_string(); + + Ok(ToolExecutionCommand::HyperwareGetApi { package_id }) + } + "hyperware_call_api" => { + let process_id = parameters + .get("process_id") + .and_then(|v| v.as_str()) + .ok_or_else(|| "Missing package_id parameter".to_string())? + .to_string(); + + let method = parameters + .get("method") + .and_then(|v| v.as_str()) + .ok_or_else(|| "Missing method parameter".to_string())? + .to_string(); + + let args = parameters + .get("args") + .and_then(|v| v.as_str()) + .ok_or_else(|| "Missing args parameter".to_string())? + .to_string(); + + let timeout = parameters + .get("timeout") + .and_then(|v| v.as_u64()) + .unwrap_or(15); + + Ok(ToolExecutionCommand::HyperwareCallApi { + process_id, + method, + args, + timeout, + }) + } + _ => Err(format!("Unknown tool: {}", tool_name)), + } + } + + fn get_provider_id(&self) -> &str { + "hyperware" + } +} + +// Helper functions for executing Hyperware operations + +pub async fn search_apis(query: &str) -> Result { + // First, get the list of all APIs from app-store + let apis_request = serde_json::to_vec(&json!("Apis")).unwrap(); + let request = Request::to(("our", "main", "app-store", "sys")) + .body(apis_request) + .expects_response(5); + + let apis_response: Value = send(request) + .await + .map_err(|e| format!("Failed to get APIs list: {:?}", e))?; + + // Extract the APIs list + let apis = apis_response + .get("ApisResponse") + .and_then(|r| r.get("apis")) + .and_then(|a| a.as_array()) + .ok_or_else(|| "Invalid APIs response format".to_string())?; + + // Process each API to get its documentation + let mut results: Vec<(String, Option)> = Vec::new(); + + for api in apis { + let package_name = api + .get("package_name") + .and_then(|n| n.as_str()) + .ok_or_else(|| "Missing package_name".to_string())?; + let publisher_node = api + .get("publisher_node") + .and_then(|n| n.as_str()) + .ok_or_else(|| "Missing publisher_node".to_string())?; + let package_id = format!("{}:{}", package_name, publisher_node); + + // Skip if package doesn't match the query (case-insensitive) + let query_lower = query.to_lowercase(); + if !package_id.to_lowercase().contains(&query_lower) { + continue; + } + + // Try to get the package documentation + match get_package_documentation(&package_id).await { + Ok(docs) => { + results.push((package_id, Some(docs))); + } + Err(_) => { + // If we can't get docs, still include the package without docs + results.push((package_id, None)); + } + } + } + + Ok(json!(results)) +} + +pub async fn get_api(package_id: &str) -> Result { + // Split package_id into package_name and publisher_node + let parts: Vec<&str> = package_id.splitn(2, ':').collect(); + if parts.len() != 2 { + return Err(format!( + "Invalid package_id format. Expected 'package_name:publisher_node', got '{}'", + package_id + )); + } + let package_name = parts[0]; + let publisher_node = parts[1]; + + // Request API zip from app-store + let get_api_request = serde_json::to_vec(&json!({ + "GetApi": { + "package_name": package_name, + "publisher_node": publisher_node, + } + })) + .unwrap(); + + let request = Request::to(("our", "main", "app-store", "sys")) + .body(get_api_request) + .expects_response(5); + + let _response: Value = send(request) + .await + .map_err(|e| format!("Failed to get API: {:?}", e))?; + + // Check if we got a blob (zip file) + let blob = get_blob(); + if blob.is_none() { + return Err(format!("No API zip found for package {}", package_id)); + } + + let blob_bytes = blob.ok_or_else(|| "No blob received".to_string())?.bytes; + + // Parse the WIT files from the zip + let resolve = parse_wit_from_zip_to_resolve(&blob_bytes, None) + .map_err(|e| format!("Failed to parse WIT files: {:?}", e))?; + + // Extract type information with full definitions and documentation + let mut types_with_definitions: Vec = Vec::new(); + let mut seen_types = std::collections::HashSet::new(); + + // Iterate through all packages in the resolve + for (_pkg_id, package) in resolve.packages.iter() { + // Process interfaces + for (iface_name, iface_id) in &package.interfaces { + let iface = &resolve.interfaces[*iface_id]; + + // Skip standard or lib interfaces but include important types + if iface_name == "standard" || iface_name == "lib" { + for (type_name, type_id) in &iface.types { + // Only include certain standard types that are commonly used + if matches!( + type_name.as_str(), + "address" + | "process-id" + | "package-id" + | "node-id" + | "capability" + | "request" + | "response" + | "message" + ) { + let rust_type_name = to_upper_camel_case(type_name); + if seen_types.insert(rust_type_name.clone()) { + let type_def = &resolve.types[*type_id]; + let docs = extract_docs(&type_def.docs); + let type_schema = type_to_json_schema(type_def, &resolve); + types_with_definitions.push(json!({ + "name": rust_type_name, + "definition": type_schema, + "documentation": docs + })); + } + } + } + continue; + } + + // Keep process name in kebab-case + let process_name = iface_name.clone(); + + // Add types within the interface + for (type_name, type_id) in &iface.types { + let type_name_camel = to_upper_camel_case(type_name); + + // Skip types ending with SignatureHttp or SignatureRemote + if type_name_camel.ends_with("SignatureHttp") + || type_name_camel.ends_with("SignatureRemote") + { + continue; + } + + if seen_types.insert(format!("{}::{}", process_name, type_name_camel)) { + let type_def = &resolve.types[*type_id]; + let docs = extract_docs(&type_def.docs); + let type_schema = type_to_json_schema(type_def, &resolve); + types_with_definitions.push(json!({ + "name": type_name_camel, + "process_name": process_name, + "definition": type_schema, + "documentation": docs + })); + } + } + + // Add functions within the interface with parameter/return type info + for (func_name, func) in &iface.functions { + let func_name_formatted = func_name.clone(); + + if seen_types.insert(format!("{}::{}", process_name, func_name_formatted)) { + let docs = extract_docs(&func.docs); + let params_schema = func + .params + .iter() + .map(|(param_name, param_type)| { + json!({ + "name": to_snake_case(param_name), + "type": type_ref_to_json(¶m_type, &resolve) + }) + }) + .collect::>(); + + let returns_schema = match &func.results { + wit_parser::Results::Named(named) => named + .iter() + .map(|(name, type_ref)| { + json!({ + "name": to_snake_case(name), + "type": type_ref_to_json(&type_ref, &resolve) + }) + }) + .collect::>(), + wit_parser::Results::Anon(type_ref) => { + vec![json!({"type": type_ref_to_json(&type_ref, &resolve)})] + } + }; + + types_with_definitions.push(json!({ + "name": func_name_formatted, + "process_name": process_name, + "type": "function", + "parameters": params_schema, + "returns": returns_schema, + "documentation": docs + })); + } + } + } + } + + Ok(json!(types_with_definitions)) +} + +pub async fn call_api( + process_id: &str, + method: &str, + args: &str, + timeout: u64, +) -> Result { + let process_id: ProcessId = process_id + .parse() + .map_err(|e: ProcessIdParseError| e.to_string())?; + + // Create request body with method and args + let request_body = serde_json::to_vec(&json!({ + method: serde_json::from_str::(args).unwrap_or_else(|_| json!(args)) + })) + .unwrap(); + + // Send the request to the package + let request = Request::to(("our", process_id)) + .body(request_body) + .expects_response(timeout); + + let response: Value = send(request) + .await + .map_err(|e| format!("Failed to call API: {:?}", e))?; + + Ok(response) +} + +async fn get_package_documentation(package_id: &str) -> Result { + // Split package_id into package_name and publisher_node + let parts: Vec<&str> = package_id.splitn(2, ':').collect(); + if parts.len() != 2 { + return Err("Invalid package_id format".to_string()); + } + let package_name = parts[0]; + let publisher_node = parts[1]; + + // Request API zip from app-store to get package-level docs + let get_api_request = serde_json::to_vec(&json!({ + "GetApi": { + "package_name": package_name, + "publisher_node": publisher_node, + } + })) + .unwrap(); + + let request = Request::to(("our", "main", "app-store", "sys")) + .body(get_api_request) + .expects_response(5); + + let _response: Value = send(request) + .await + .map_err(|e| format!("Failed to get API: {:?}", e))?; + + // Check if we got a blob (zip file) + let blob = get_blob(); + if blob.is_none() { + return Err(format!("No API zip found for package {}", package_id)); + } + + let blob_bytes = blob.ok_or_else(|| "No blob received".to_string())?.bytes; + + // Parse the WIT files from the zip + let resolve = parse_wit_from_zip_to_resolve(&blob_bytes, None) + .map_err(|e| format!("Failed to parse WIT files: {:?}", e))?; + + // Try to find package-level documentation + // Look through packages for one matching our package name + for (_pkg_id, package) in resolve.packages.iter() { + let pkg_name = &package.name; + if pkg_name.name.contains(package_name) { + // Check if package has documentation + // Note: wit-parser Package doesn't have a docs field directly + // Package docs would typically be in a README or main interface + // For now, return formatted package info + return Ok(format!( + "Package: {} - Provides API interfaces and types", + pkg_name.name + )); + } + } + + // Fallback to basic description + Ok(format!("API package: {}", package_name)) +} + +fn extract_docs(docs: &Docs) -> Option { + docs.contents.clone() +} + +// Helper function to convert snake_case to UpperCamelCase +fn to_upper_camel_case(s: &str) -> String { + s.split('-') + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().collect::() + chars.as_str(), + } + }) + .collect::() +} + +// Helper function to convert kebab-case to snake_case +fn to_snake_case(s: &str) -> String { + s.replace('-', "_") +} + +// Convert a WIT type definition to a JSON schema representation +fn type_to_json_schema(type_def: &wit_parser::TypeDef, resolve: &wit_parser::Resolve) -> Value { + use wit_parser::TypeDefKind; + + match &type_def.kind { + TypeDefKind::Record(record) => { + let fields = record + .fields + .iter() + .map(|field| { + ( + to_snake_case(&field.name), + type_ref_to_json(&field.ty, resolve), + ) + }) + .collect::>(); + + json!({ + "type": "object", + "properties": fields + }) + } + TypeDefKind::Variant(variant) => { + let cases = variant + .cases + .iter() + .map(|case| { + let case_schema = match &case.ty { + Some(ty) => type_ref_to_json(ty, resolve), + None => json!("null"), + }; + json!({ + "name": case.name, + "type": case_schema + }) + }) + .collect::>(); + + json!({ + "type": "variant", + "cases": cases + }) + } + TypeDefKind::Enum(enum_def) => { + let cases = enum_def + .cases + .iter() + .map(|case| &case.name) + .collect::>(); + json!({ + "type": "enum", + "values": cases + }) + } + TypeDefKind::List(ty) => { + json!({ + "type": "array", + "items": type_ref_to_json(ty, resolve) + }) + } + TypeDefKind::Tuple(tuple) => { + let types = tuple + .types + .iter() + .map(|ty| type_ref_to_json(ty, resolve)) + .collect::>(); + json!({ + "type": "tuple", + "items": types + }) + } + TypeDefKind::Option(ty) => { + json!({ + "type": "option", + "value": type_ref_to_json(ty, resolve) + }) + } + TypeDefKind::Result(result) => { + json!({ + "type": "result", + "ok": result.ok.as_ref().map(|ty| type_ref_to_json(ty, resolve)), + "err": result.err.as_ref().map(|ty| type_ref_to_json(ty, resolve)) + }) + } + TypeDefKind::Flags(flags) => { + let flag_names = flags + .flags + .iter() + .map(|flag| &flag.name) + .collect::>(); + json!({ + "type": "flags", + "flags": flag_names + }) + } + TypeDefKind::Type(ty) => type_ref_to_json(ty, resolve), + _ => json!("unknown"), + } +} + +// Convert a WIT type reference to a JSON representation +fn type_ref_to_json(type_ref: &wit_parser::Type, resolve: &wit_parser::Resolve) -> Value { + use wit_parser::Type; + + match type_ref { + Type::Bool => json!("bool"), + Type::U8 => json!("u8"), + Type::U16 => json!("u16"), + Type::U32 => json!("u32"), + Type::U64 => json!("u64"), + Type::S8 => json!("s8"), + Type::S16 => json!("s16"), + Type::S32 => json!("s32"), + Type::S64 => json!("s64"), + Type::F32 => json!("f32"), + Type::F64 => json!("f64"), + Type::Char => json!("char"), + Type::String => json!("string"), + Type::Id(id) => { + // Look up the referenced type + if let Some(type_def) = resolve.types.get(*id) { + // If it has a name, use the name; otherwise, inline the definition + if let Some(name) = &type_def.name { + json!(to_upper_camel_case(name)) + } else { + type_to_json_schema(type_def, resolve) + } + } else { + json!("unknown") + } + } + } +} diff --git a/hyperdrive/packages/spider/spider/src/tool_providers/mod.rs b/hyperdrive/packages/spider/spider/src/tool_providers/mod.rs index 9710b302d..85816d330 100644 --- a/hyperdrive/packages/spider/spider/src/tool_providers/mod.rs +++ b/hyperdrive/packages/spider/spider/src/tool_providers/mod.rs @@ -1,5 +1,6 @@ pub mod build_container; pub mod hypergrid; +pub mod hyperware; use crate::types::{SpiderState, Tool}; use serde_json::Value; @@ -47,6 +48,19 @@ pub enum ToolExecutionCommand { provider_name: String, call_args: Vec<(String, String)>, }, + // Hyperware commands + HyperwareSearchApis { + query: String, + }, + HyperwareGetApi { + package_id: String, + }, + HyperwareCallApi { + process_id: String, + method: String, + args: String, + timeout: u64, + }, // Direct result (for synchronous operations) DirectResult(Result), } diff --git a/hyperdrive/src/http/server.rs b/hyperdrive/src/http/server.rs index ee8d008ea..3c47e5653 100644 --- a/hyperdrive/src/http/server.rs +++ b/hyperdrive/src/http/server.rs @@ -765,6 +765,12 @@ async fn http_handler( } } + let timeout = headers + .get("X-Hyperware-Http-Server-Timeout") + .and_then(|t| t.to_str().ok()) + .and_then(|t| t.parse().ok()) + .unwrap_or_else(|| HTTP_SELF_IMPOSED_TIMEOUT); + // RPC functionality: if path is /rpc:distro:sys/message, // we extract message from base64 encoded bytes in data // and send it to the correct app. @@ -796,7 +802,7 @@ async fn http_handler( rsvp: None, message: Message::Request(Request { inherit: false, - expects_response: Some(HTTP_SELF_IMPOSED_TIMEOUT), + expects_response: Some(timeout.clone()), body: serde_json::to_vec(&HttpServerRequest::Http(IncomingHttpRequest { source_socket_addr: socket_addr.map(|addr| addr.to_string()), method: method.to_string(), @@ -836,7 +842,7 @@ async fn http_handler( message.send(&send_to_loop).await; - let timeout_duration = tokio::time::Duration::from_secs(HTTP_SELF_IMPOSED_TIMEOUT); + let timeout_duration = tokio::time::Duration::from_secs(timeout); let result = tokio::time::timeout(timeout_duration, response_receiver).await; let (http_response, body) = match result {