diff --git a/.DS_Store b/.DS_Store index fdf8920e..e2d3b266 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index ce3b4fe4..af45e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,18 @@ -*/target/ -/target -pkg/*.wasm -!pkg/tg.wasm -!pkg/openai.wasm -!pkg/speech_to_text.wasm -!pkg/command_center.wasm +.DS_Store + +**/target/ +*.wasm *.swp *.swo */wasi_snapshot_preview1.wasm */wit/ */process_env + +/command_center/!pkg/*.wasm +/command_center/pkg/ui/* +/command_center/pkg/node_modules +/command_center/pkg/dist +/command_center/ui/node_modules +/command_center/ui/package-lock.json +/command_center/ui/yarn.lock + diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 53479a64..00000000 --- a/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[workspace] -resolver = "2" -members = [ - "command_center", -] - -[profile.release] -panic = "abort" -opt-level = "s" -lto = true diff --git a/README.md b/README.md index f9d69dde..57a1439a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,142 @@ # Command Center -Work in Progress. In the meantime, enjoy an image of a Command Center. +### Directory Structure -![Command Center Image](command_center.jpeg) +``` +command_center +│ ├── README.md +│ ├── command_center +│ │ ├── ai_chatbot_demo +│ │ ├── command_center +│ │ ├── pkg +│ │ ├── ui +│ │ └── worker +│ ├── files_lib +``` + +To disambiguate the 3 levels of `command_center`: +- outermost - `command_center` the git repo +- middle - `command_center` the package +- innermost - `command_center` the process + +`files_lib` is a package, used as a shared library by the `command_center` and `worker` processes. + +### Quick Start + +For the purposes of this tutorial: +- `node.os` is at `home` +- `node2.os` is at `home2` + +Boot up 2 real nodes. Real ones are preferable because we are working with file storage which is easier to persist this way. + +```bash +./binary/kinode home +./binary/kinode home2 +``` + +```bash +cd command_center/command_center +kit b && kit s && kit s -p 8081 +``` +- due to a current bug in kit, you can use `kit b <<< y && kit s && kit s -p 8081` instead, if you don't want to manually press 'y' every time + +```bash +cd command_center/command_center/ui +npm i +npm run dev +``` + +### Working Functionality + +In the UI you should be able to use the following tabs: +- Config - set up tg bot +- Data Center - see tg chat in real time +- Import Notes - import notes via ui +- Notes - check backup status, search notes, view directory structure, and view notes in markdown +- Provided backups - see backups which you are providing for other nodes + +UI functionality related to backups is a work in progress, so it may not work correctly. + +### Dev Setup for Backups + +`node.os` at `home` folder will be backing up their notes to `node2.os` at `home2` folder. + +Import notes via ui on `node.os`, they should show up here: +```bash +cd home/vfs/command_center:appattacc.os/files +ls +``` + +Throughout the rest of the tutorial, replace `node.os` and `node2.os` with the node ids of your nodes. + +#### Backing Up + +To back up the notes, in `node.os` terminal, run: +``` +m node.os@main:command_center:appattacc.os '{"BackupRequest": {"node_id": "node2.os", "size": 0, "password_hash": "somehash"}}' +``` + +Wait until in the `node2.os` terminal you see a message like the following: +``` +command_center:appattacc.os: command_center worker: done: exiting, took 123.456ms +``` + +In `node2.os` `home2`, you should find a folder called `node.os`: +``` bash +cd home2/vfs/command_center:appattacc.os/encrypted_storage/ +ls +``` + +Inside that folder should be a bunch of encrypted files +```bash +cd node.os +ls +``` + +#### Retrieving Backup + +To retrieve the backup to `node.os`, in `node.os` terminal, run: +```bash +m node.os@main:command_center:appattacc.os '{"BackupRetrieve": {"node_id": "node2.os"}}' +``` + +Wait until in the `node1.os` terminal you see a message like the following: +``` +command_center:appattacc.os: command_center worker: done: exiting, took 123.456ms +``` + +In `node.os` `home`, you should find a folder called `retrieved_encrypted_backup` containing the same encrypted files which are backed up to `node2.os`: +```bash +cd home/vfs/command_center:appattacc.os/retrieved_encrypted_backup +ls +``` + +#### Decrypting Retrieved Backup + +Warning: this will overwrite any changes to the notes you made in the `files` directory in the meantime. + +To decrypt this data, in `node.os` terminal, run: +```bash +m node.os@main:command_center:appattacc.os '{"Decrypt": {"password_hash": "somehash"}}' +``` + +In `node.os` `home`, look at `files`, they should contain the decrypted data. +```bash +cd home/vfs/command_center:appattacc.os/files +ls +``` + +### TODO Functionality +- the commands shown above need to be connected to the ui +- the ui also needs to correctly surface backup status + - for client node: when was the last time data was backed up + - for server node: all nodes which are backing up data to this node, and corresponding statuses +- a setting which allows a node to say: "I provide backups" or "I do not provide backups". Additionally, whitelisting and blacklisting by arbitrary criteria can be done. +- editing notes via markdown and propagating the changes to backend, then to backup +- vector search your notes + +### Backup Diagrams + +- [ClientRequest::BackupRequest](https://swimlanes.io/#jZFBUsMwDEX3PoUOQDmAFywIG7bJMCyJxxFUE1dObRlmenocp9AWp8DO0vz//fwtJA413Bs7Er/B06SUDwMGDdYRssCHDyOGm6/R7oYXmw/zKmJ4x7C2WkxKVSbY3NU2DX1ThC3uE0bResZJ03HslaosZznLXTnjuRy+Mx6ZhIyjA7ZokbJ0EVyLq1hzZFeELcbJc8QfXKd14/mVwq6/8uCLJn8D7ZCHM8wL3z9e3GwTjz0o9oJzp2WMYAJmZw4SD7JFCKWO+bePyYmFHBjnYDBigGKR364ArHW0QHRiJEWtHzzjqeEK/U/nJw==) + +- [ClientRequest::BackupRetrieve](https://swimlanes.io/#jZE9TgQxDIX7nMIHAA6QgoLdhnamoGSizBMbTUiWxFkkTk8m2V8SBJ1t+X16z2bDFpIGcDA4GPdGT0ovaS+EDzOCJG0NHNOnDwvC3anV7/OrzsU6iggHhN6oioRoRHT/eAuWNL2UYsBHQmQpn51ho6z5wgANk3F1YfoF17jIyE1ZPCNrsmNUTCKKRnRF+tvYCDdf2erCGq8ZOJbFAXHvXcRPX6f5hVid/ONmm11yy0TCecYav7SRVEAOlXXsiXegUA66/vpITo6NJWUtzYoVmVjWH86XbgzcxKkmRlacopRb77rWe//pKsU3) \ No newline at end of file diff --git a/Cargo.lock b/command_center/Cargo.lock similarity index 78% rename from Cargo.lock rename to command_center/Cargo.lock index d2e93b0d..0f16d66d 100644 --- a/Cargo.lock +++ b/command_center/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -26,12 +26,65 @@ dependencies = [ "memchr", ] +[[package]] +name = "ai_chatbot_demo" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "frankenstein", + "kinode_process_lib 0.8.0", + "llm_interface", + "serde", + "serde_json", + "stt_interface", + "telegram_interface", + "wit-bindgen 0.24.0", +] + +[[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 0.7.6", + "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 0.7.6", + "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 0.7.6", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-json-rpc" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy.git?rev=098ad56#098ad5657d55bbc5fe9469ede2a9ca79def738f2" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.6.4", "serde", "serde_json", "thiserror", @@ -40,12 +93,13 @@ dependencies = [ [[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", + "alloy-primitives 0.7.6", "serde", "serde_json", "thiserror", + "tracing", ] [[package]] @@ -70,11 +124,33 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "alloy-primitives" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand 0.8.5", + "ruint", + "serde", + "tiny-keccak", +] + [[package]] name = "alloy-rlp" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +checksum = "b155716bab55763c95ba212806cf43d05bcc70e5f35b02bad20cf5ec7fe11fed" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -83,13 +159,13 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" +checksum = "8037e03c7f462a063f28daec9fda285a9a89da003c552f8637a80b9c8fd96241" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -97,7 +173,7 @@ name = "alloy-rpc-types" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy.git?rev=098ad56#098ad5657d55bbc5fe9469ede2a9ca79def738f2" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.6.4", "alloy-rlp", "itertools 0.12.1", "serde", @@ -108,16 +184,89 @@ 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-primitives", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives 0.7.6", "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 0.7.6", + "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 0.7.6", + "alloy-sol-macro", + "const-hex", +] + [[package]] name = "alloy-transport" version = "0.1.0" @@ -130,24 +279,25 @@ dependencies = [ "thiserror", "tokio", "tower", - "url 2.5.0", + "url 2.5.1", "wasm-bindgen-futures", ] [[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 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4)", - "base64 0.21.7", + "alloy-json-rpc 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=cad7935)", + "base64 0.22.1", "futures-util", + "futures-utils-wasm", "serde", "serde_json", "thiserror", "tokio", "tower", - "url 2.5.0", + "url 2.5.1", "wasm-bindgen-futures", ] @@ -168,9 +318,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "ark-ff" @@ -316,7 +466,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -336,9 +486,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -355,6 +505,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557" +dependencies = [ + "byteorder", +] + [[package]] name = "base64" version = "0.9.3" @@ -371,6 +530,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" @@ -434,6 +599,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 = "buf_redux" version = "0.8.4" @@ -471,11 +648,25 @@ dependencies = [ "serde", ] +[[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 0.4.3", + "libc", + "serde", +] + [[package]] name = "cc" -version = "1.0.97" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -493,6 +684,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets", ] @@ -517,27 +709,30 @@ name = "command_center" version = "0.1.0" dependencies = [ "anyhow", + "base64 0.22.1", "bincode", + "chrono", + "files_lib", "frankenstein", - "kinode_process_lib 0.6.0 (git+https://github.com/kinode-dao/process_lib?rev=84b3d84)", + "kinode_process_lib 0.8.0", "llm_interface", "serde", "serde_json", "stt_interface", "telegram_interface", - "url 2.5.0", + "url 2.5.1", "wit-bindgen 0.24.0", ] [[package]] name = "const-hex" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", - "hex", + "hex 0.4.3", "proptest", "serde", ] @@ -599,9 +794,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -609,27 +804,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -671,7 +866,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -681,20 +876,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -718,6 +913,23 @@ dependencies = [ "subtle", ] +[[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" @@ -734,9 +946,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elliptic-curve" @@ -800,6 +1012,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "files_lib" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "chrono", + "kinode_process_lib 0.8.0", + "rand 0.8.5", + "rust-crypto-wasm", + "serde", + "serde_json", + "wit-bindgen 0.24.0", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -864,7 +1091,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -887,6 +1114,18 @@ 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 = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + [[package]] name = "generic-array" version = "0.14.7" @@ -911,9 +1150,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "group" @@ -947,12 +1192,24 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" + [[package]] name = "hex" version = "0.4.3" @@ -990,9 +1247,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "hyper" @@ -1036,6 +1293,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" @@ -1061,12 +1436,14 @@ dependencies = [ [[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]] @@ -1179,7 +1556,7 @@ version = "0.6.0" source = "git+https://github.com/kinode-dao/process_lib?rev=3232423#323242399efdcdad02e7f31bb6a9cc5eec048610" dependencies = [ "alloy-json-rpc 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=098ad56)", - "alloy-primitives", + "alloy-primitives 0.6.4", "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=098ad56)", "alloy-transport 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=098ad56)", "anyhow", @@ -1190,28 +1567,29 @@ dependencies = [ "serde", "serde_json", "thiserror", - "url 2.5.0", + "url 2.5.1", "wit-bindgen 0.17.0", ] [[package]] name = "kinode_process_lib" -version = "0.6.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=84b3d84#84b3d84c7c31185f15691a288f1b45dbffb18fe2" +version = "0.8.0" +source = "git+https://github.com/kinode-dao/process_lib?tag=v0.8.1#847188a758adc45b490f08d4352fdc35110fb8ec" dependencies = [ - "alloy-json-rpc 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4)", - "alloy-primitives", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4)", - "alloy-transport 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4)", + "alloy-json-rpc 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=cad7935)", + "alloy-primitives 0.7.6", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=cad7935)", + "alloy-transport 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=cad7935)", "anyhow", "bincode", "http", "mime_guess 2.0.4", "rand 0.8.5", + "rmp-serde", "serde", "serde_json", "thiserror", - "url 2.5.0", + "url 2.5.1", "wit-bindgen 0.24.0", ] @@ -1235,9 +1613,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -1247,9 +1625,15 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "llm_interface" @@ -1285,9 +1669,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -1328,9 +1712,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -1435,9 +1819,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1559,7 +1943,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1619,11 +2003,35 @@ dependencies = [ "toml_edit", ] +[[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 0.9.4", +] + +[[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 0.9.4", +] + [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -1669,6 +2077,29 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.6.5" @@ -1825,9 +2256,9 @@ 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", @@ -1837,9 +2268,9 @@ 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", @@ -1848,9 +2279,9 @@ dependencies = [ [[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 = "rfc6979" @@ -1872,11 +2303,33 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[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", @@ -1898,9 +2351,23 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.2.0" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rust-crypto-wasm" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" +checksum = "9dcf11edbc9a0effb4a99ddbe909dd26fb2e71459064879218c27b0add1cb6ec" +dependencies = [ + "base64 0.5.2", + "gcc", + "hex 0.2.0", + "libc", + "rand 0.3.23", + "time", +] [[package]] name = "rustc-demangle" @@ -2009,22 +2476,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2092,9 +2559,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[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", ] @@ -2109,6 +2576,12 @@ dependencies = [ "der", ] +[[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" @@ -2117,9 +2590,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "stt_interface" @@ -2132,9 +2605,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -2149,15 +2622,38 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.61" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "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 = "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 = "tap" version = "1.0.1" @@ -2167,16 +2663,16 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "telegram_interface" version = "0.1.0" -source = "git+https://github.com/kinode-dao/telegram-bot/?branch=zen/interface#683cdc8b931733c9c15d283f99230d23dee9bd4e" +source = "git+https://github.com/kinode-dao/telegram-bot/?branch=zen/interface#12e04debb9cd7ffb9a707b0227f08150588d6c89" dependencies = [ "anyhow", "bincode", "frankenstein", - "kinode_process_lib 0.6.0 (git+https://github.com/kinode-dao/process_lib?rev=3232423)", + "kinode_process_lib 0.6.0", "multipart", "serde", "serde_json", - "url 2.5.0", + "url 2.5.1", "wit-bindgen 0.17.0", ] @@ -2194,22 +2690,31 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", ] [[package]] @@ -2245,6 +2750,16 @@ dependencies = [ "url 1.7.2", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2262,9 +2777,9 @@ 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", "pin-project-lite", @@ -2272,9 +2787,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" [[package]] name = "toml_edit" @@ -2322,9 +2837,21 @@ checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log 0.4.21", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -2372,7 +2899,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2404,7 +2931,7 @@ checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", - "hex", + "hex 0.4.3", "static_assertions", ] @@ -2487,15 +3014,27 @@ dependencies = [ [[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 0.5.0", + "idna 1.0.0", "percent-encoding 2.3.1", ] +[[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 = "valuable" version = "0.1.0" @@ -2556,7 +3095,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -2590,7 +3129,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2850,7 +3389,7 @@ version = "0.17.0" source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=21a46c7#21a46c774532da99384f7a1877c1fcfb7a4c72d3" dependencies = [ "anyhow", - "heck", + "heck 0.4.1", "wasm-metadata 0.10.20", "wit-bindgen-core 0.17.0", "wit-component 0.20.3", @@ -2863,7 +3402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30acbe8fb708c3a830a33c4cb705df82659bf831b492ec6ca1a17a369cfeeafb" dependencies = [ "anyhow", - "heck", + "heck 0.4.1", "indexmap", "wasm-metadata 0.202.0", "wit-bindgen-core 0.24.0", @@ -2878,7 +3417,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wit-bindgen-core 0.17.0", "wit-bindgen-rust 0.17.0", "wit-component 0.20.3", @@ -2893,7 +3432,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wit-bindgen-core 0.24.0", "wit-bindgen-rust 0.24.0", ] @@ -2971,6 +3510,37 @@ dependencies = [ "wasmparser 0.202.0", ] +[[package]] +name = "worker" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bincode", + "files_lib", + "frankenstein", + "kinode_process_lib 0.8.0", + "llm_interface", + "serde", + "serde_json", + "stt_interface", + "telegram_interface", + "url 2.5.1", + "wit-bindgen 0.24.0", +] + +[[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" @@ -2980,11 +3550,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", ] @@ -2997,5 +3612,27 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "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", ] diff --git a/command_center/Cargo.toml b/command_center/Cargo.toml index 7223d0b1..af613e6b 100644 --- a/command_center/Cargo.toml +++ b/command_center/Cargo.toml @@ -1,24 +1,12 @@ -[package] -name = "command_center" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow = "1.0" -bincode = "1.3.3" -frankenstein = { version = "0.30", default-features = false, features = ["telegram-trait"] } -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "84b3d84" } -llm_interface = { git = "https://github.com/kinode-dao/llm", branch = "interface"} -stt_interface = { git = "https://github.com/kinode-dao/stt", branch = "interface"} -telegram_interface = { git = "https://github.com/kinode-dao/telegram-bot/", branch = "zen/interface" } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -url = "2.5.0" - -wit-bindgen = "0.24.0" - -[lib] -crate-type = ["cdylib"] - -[package.metadata.component] -package = "kinode:process" +[workspace] +resolver = "2" +members = [ + "command_center", + "ai_chatbot_demo", + "worker" +] + +[profile.release] +panic = "abort" +opt-level = "s" +lto = true diff --git a/command_center/ai_chatbot_demo/Cargo.toml b/command_center/ai_chatbot_demo/Cargo.toml new file mode 100644 index 00000000..7d91274d --- /dev/null +++ b/command_center/ai_chatbot_demo/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ai_chatbot_demo" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +bincode = "1.3.3" +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.1" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +wit-bindgen = "0.24.0" +llm_interface = { git = "https://github.com/kinode-dao/llm", branch = "interface"} +stt_interface = { git = "https://github.com/kinode-dao/stt", branch = "interface"} +telegram_interface = { git = "https://github.com/kinode-dao/telegram-bot/", branch = "zen/interface" } +# TODO: We probably shouldn't have frankenstein here, the telegram_interface (or the wit) should contain all the traits required. +frankenstein = { version = "0.30", default-features = false, features = ["telegram-trait"] } + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/command_center/ai_chatbot_demo/src/lib.rs b/command_center/ai_chatbot_demo/src/lib.rs new file mode 100644 index 00000000..391a2eae --- /dev/null +++ b/command_center/ai_chatbot_demo/src/lib.rs @@ -0,0 +1,165 @@ +use kinode_process_lib::{ + await_message, call_init, Address, Message, get_blob, Request, /*println */ +}; +use frankenstein::{Message as TgMessage, UpdateContent, ChatId, SendMessageParams}; + +use frankenstein::GetFileParams; +use llm_interface::openai::*; +use stt_interface::STTRequest; +use stt_interface::STTResponse; +use telegram_interface::*; + +pub const TG_ADDRESS: (&str, &str, &str, &str) = ("our", "tg", "command_center", "appattacc.os"); +pub const LLM_ADDRESS: (&str, &str, &str, &str) = + ("our", "openai", "command_center", "appattacc.os"); +pub const STT_ADDRESS: (&str, &str, &str, &str) = + ("our", "speech_to_text", "command_center", "appattacc.os"); + +wit_bindgen::generate!({ + path: "target/wit", + world: "process-v0", +}); + +fn handle_message( + our: &Address, +) -> anyhow::Result<()> { + println!("chatbot: handle message"); + let message = await_message()?; + if message.source().node != our.node { + println!("chatbot: message from wrong node"); + return Ok(()); + } + println!("chatbot: message from {:?}", message.source()); + println!("chatbot: {:#?}", message); + handle_telegram_message(&message) +} + +pub fn handle_telegram_message(message: &Message) -> anyhow::Result<()> { + println!("chatbot: handle telegram message"); + let Some(msg) = get_last_tg_msg(&message) else { + return Ok(()); + }; + let id = msg.chat.id; + let mut text = msg.text.clone().unwrap_or_default(); + if let Some(voice) = msg.voice.clone() { + let audio = get_file(&voice.file_id)?; + text += &get_text(audio)?; + println!("chatbot: text: {}", &text); + } + let answer = get_groq_answer(&text)?; + let _message = send_bot_message(&answer, id); + Ok(()) +} + +fn send_bot_message(text: &str, id: i64) -> anyhow::Result { + println!("chatbot: send bot message: {}", text); + let params = SendMessageParams::builder() + .chat_id(ChatId::Integer(id)) + .text(text) + .build(); + let send_message_request = serde_json::to_vec(&TgRequest::SendMessage(params))?; + let response = Request::to(TG_ADDRESS) + .body(send_message_request) + .send_and_await_response(30)??; + let TgResponse::SendMessage(message) = serde_json::from_slice(response.body())? else { + println!("chatbot: failed to send message"); + return Err(anyhow::anyhow!("Failed to send message")); + }; + Ok(message) +} + +fn get_groq_answer(text: &str) -> anyhow::Result { + println!("chatbot: get groq answer"); + let request = ChatRequestBuilder::default() + .model("llama3-8b-8192".to_string()) + .messages(vec![MessageBuilder::default() + .role("user".to_string()) + .content(text.to_string()) + .build()?]) + .build()?; + let request = serde_json::to_vec(&LLMRequest::GroqChat(request))?; + let response = Request::to(LLM_ADDRESS) + .body(request) + .send_and_await_response(30)??; + let LLMResponse::Chat(chat) = serde_json::from_slice(response.body())? else { + println!("chatbot: failed to parse LLM response"); + return Err(anyhow::anyhow!("Failed to parse LLM response")); + }; + println!("{:?}", chat.choices[0].message.content.clone()); + Ok(chat.choices[0].message.content.clone()) +} + +fn get_text(audio: Vec) -> anyhow::Result { + println!("chatbot: get text"); + let stt_request = serde_json::to_vec(&STTRequest::OpenaiTranscribe(audio))?; + let response = Request::to(STT_ADDRESS) + .body(stt_request) + .send_and_await_response(3)??; + let STTResponse::OpenaiTranscribed(text) = serde_json::from_slice(response.body())? else { + println!("chatbot: failed to parse STT response"); + return Err(anyhow::anyhow!("Failed to parse STT response")); + }; + Ok(text) +} + +fn get_file(file_id: &str) -> anyhow::Result> { + println!("chatbot: get file"); + let get_file_params = GetFileParams::builder().file_id(file_id).build(); + let tg_request = serde_json::to_vec(&TgRequest::GetFile(get_file_params))?; + let _ = Request::to(TG_ADDRESS) + .body(tg_request) + .send_and_await_response(10)??; + if let Some(blob) = get_blob() { + println!("chatbot: got file successfully"); + return Ok(blob.bytes); + } + println!("chatbot: failed to get file"); + Err(anyhow::anyhow!("Failed to get file")) +} + +fn get_last_tg_msg(message: &Message) -> Option { + println!("chatbot: get last tg msg"); + let Ok(TgResponse::Update(tg_update)) = serde_json::from_slice(message.body()) else { + println!("chatbot: failed to parse tg update from json"); + return None; + }; + let update = tg_update.updates.last()?; + let msg = match &update.content { + UpdateContent::Message(msg) | UpdateContent::ChannelPost(msg) => msg, + _ => { + println!("chatbot: failed to parse tg update"); + return None; + } + }; + println!("{:?}", &msg); + Some(msg.clone()) +} + +pub fn subscribe() -> anyhow::Result<()> { + println!("chatbot: subscribing to telegram"); + let subscribe_request = serde_json::to_vec(&TgRequest::Subscribe)?; + let result = Request::to(TG_ADDRESS) + .body(subscribe_request) + .send_and_await_response(15)??; + let TgResponse::Ok = serde_json::from_slice::(result.body())? else { + println!("chatbot: failed to parse subscription response"); + return Err(anyhow::anyhow!("Failed to parse subscription response")); + }; + println!("chatbot: subscribed to telegram"); + Ok(()) +} + +call_init!(init); +fn init(our: Address) { + let _ = subscribe(); + println!("chatbot: init"); + loop { + match handle_message(&our) { + Ok(_) => {} + Err(e) => println!("Error: {:?}", e), + } + } +} + +// TODO: Requires context manager +// TODO: Make stuff async diff --git a/command_center.jpeg b/command_center/command_center.jpeg similarity index 100% rename from command_center.jpeg rename to command_center/command_center.jpeg diff --git a/command_center/command_center/Cargo.toml b/command_center/command_center/Cargo.toml new file mode 100644 index 00000000..7313e2bf --- /dev/null +++ b/command_center/command_center/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "command_center" +version = "0.1.0" +edition = "2021" + +[dependencies] +files_lib = {path = "../../files_lib"} +chrono = { version = "0.4.38", features = ["serde"] } +base64 = "0.22.1" +anyhow = "1.0" +bincode = "1.3.3" +frankenstein = { version = "0.30", default-features = false, features = ["telegram-trait"] } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.8.1" } +llm_interface = { git = "https://github.com/kinode-dao/llm", branch = "interface"} +stt_interface = { git = "https://github.com/kinode-dao/stt", branch = "interface"} +telegram_interface = { git = "https://github.com/kinode-dao/telegram-bot/", branch = "zen/interface" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +url = "2.5.0" + +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/command_center/src/icon b/command_center/command_center/src/icon similarity index 100% rename from command_center/src/icon rename to command_center/command_center/src/icon diff --git a/command_center/command_center/src/lib.rs b/command_center/command_center/src/lib.rs new file mode 100644 index 00000000..12afb415 --- /dev/null +++ b/command_center/command_center/src/lib.rs @@ -0,0 +1,788 @@ +use base64::{engine::general_purpose, Engine as _}; +use std::collections::HashMap; +use std::path::Path; + +use kinode_process_lib::vfs::{ + create_drive, create_file, open_dir, open_file, DirEntry, FileType, SeekFrom, VfsAction, + VfsRequest, +}; +use kinode_process_lib::{ + await_message, call_init, get_blob, http, our_capabilities, println, spawn, Address, Message, + OnExit, Request, Response, LazyLoadBlob +}; + +use kinode_process_lib::http::{ + WsMessageType, send_ws_push, bind_ws_path +}; + +use llm_interface::openai::*; +use stt_interface::*; +use telegram_interface::*; + +mod structs; +use structs::*; + +mod tg_api; + +use files_lib::encryption::{decrypt_data, ENCRYPTED_CHUNK_SIZE}; +use files_lib::structs::{ + BackupRequestResponse, ClientRequest, ServerResponse, UiRequest, WorkerRequest, WorkerStatus, +}; +use files_lib::{import_notes, read_nested_dir_light}; + +wit_bindgen::generate!({ + path: "target/wit", + world: "process-v0", +}); + +///////////////////////////////////////////////// +// functions that fulfill HTTP requests from UI + +fn fetch_backup_data(state: &mut State) -> anyhow::Result<()> { + let backup_data = serde_json::to_vec(&serde_json::json!({ + "backups_time_map": state.backup_info.backups_time_map, + "notes_last_backed_up_at": state.backup_info.notes_last_backed_up_at, + "notes_backup_provider": state.backup_info.notes_backup_provider, + }))?; + http::send_response( + http::StatusCode::OK, + Some(HashMap::from([( + "Content-Type".to_string(), + "application/json".to_string(), + )])), + backup_data, + ); + Ok(()) +} + +fn fetch_api_keys(state: &mut State) -> anyhow::Result<()> { + let config = &state.api_keys; + let response_body = serde_json::to_string(&config)?; + http::send_response( + http::StatusCode::OK, + Some(HashMap::from([( + "Content-Type".to_string(), + "application/json".to_string(), + )])), + response_body.as_bytes().to_vec(), + ); + Ok(()) +} + +fn fetch_notes(our_files_path: &String) -> anyhow::Result<()> { + let dir_entry: DirEntry = DirEntry { + path: our_files_path.to_string(), + file_type: FileType::Directory, + }; + + let notes = files_lib::read_nested_dir(dir_entry)?; + + let response_body = serde_json::to_string(¬es)?; + http::send_response( + http::StatusCode::OK, + Some(HashMap::from([( + "Content-Type".to_string(), + "application/json".to_string(), + )])), + response_body.as_bytes().to_vec(), + ); + Ok(()) +} + +fn submit_api_keys( + state: &mut State, + pkgs: &HashMap, + body_bytes: &[u8], +) -> anyhow::Result<()> { + let api_keys = serde_json::from_slice::(body_bytes)?; + println!("Modifying api_keys to {:?}", api_keys); + state.api_keys = api_keys; + + for (pkg, addr) in pkgs.iter() { + println!("submit_api_keys: matching pkg: {:?}", pkg); + match pkg { + Pkg::LLM => { + if let Some(openai_key) = &state.api_keys.openai_key { + let req = serde_json::to_vec(&LLMRequest::RegisterOpenaiApiKey( + RegisterApiKeyRequest { + api_key: openai_key.clone(), + }, + ))?; + let _ = Request::new() + .target(addr.clone()) + .body(req) + .send_and_await_response(5)??; + } + if let Some(groq_key) = &state.api_keys.groq_key { + let req = serde_json::to_vec( + &llm_interface::openai::LLMRequest::RegisterGroqApiKey( + RegisterApiKeyRequest { + api_key: groq_key.clone(), + }, + ), + )?; + let _ = Request::new() + .target(addr.clone()) + .body(req) + .send_and_await_response(5)??; + } + } + Pkg::STT => { + if let Some(openai_key) = &state.api_keys.openai_key { + let req = serde_json::to_vec(&STTRequest::RegisterApiKey(openai_key.clone()))?; + let _ = Request::new() + .target(addr.clone()) + .body(req) + .send_and_await_response(5)??; + } + } + Pkg::Telegram => { + if let Some(telegram_key) = &state.api_keys.telegram_key { + let init = TgInitialize { + token: telegram_key.clone(), + params: None, + }; + let req = serde_json::to_vec(&TgRequest::RegisterApiKey(init))?; + let _ = Request::new() + .target(addr.clone()) + .body(req) + .send_and_await_response(5)??; + } + } + } + } + state.save(); + + http::send_response( + http::StatusCode::OK, + Some(HashMap::from([( + "Content-Type".to_string(), + "application/json".to_string(), + )])), + b"{\"message\": \"success\"}".to_vec(), + ); + Ok(()) +} + +fn import_notes_from_ui(body_bytes: &[u8], import_to: &String) -> anyhow::Result<()> { + let directory: HashMap = + serde_json::from_slice::>(body_bytes)?; + if import_notes(directory, import_to).is_ok() { + http::send_response( + http::StatusCode::OK, + Some(HashMap::from([( + "Content-Type".to_string(), + "application/json".to_string(), + )])), + b"{\"message\": \"success\"}".to_vec(), + ); + Ok(()) + } else { + Err(anyhow::anyhow!("Failed to import notes")) + } +} + +///////////////////////////////////////////////// + +// spawns a worker process for file transfer (whether it will be for receiving or sending) +fn initialize_worker( + our: Address, + current_worker_address: &mut Option
, +) -> anyhow::Result<()> { + let our_worker = spawn( + None, + &format!("{}/pkg/worker.wasm", our.package_id()), + OnExit::None, + our_capabilities(), + vec![], + false, + )?; + + // temporarily stores worker address as while the worker is alive + *current_worker_address = Some(Address { + node: our.node.clone(), + process: our_worker.clone(), + }); + Ok(()) +} + +// for making backup related requests from the ui +// currently, terminal requests are funneled to this, as the ui requests are not built yet. see `fn handle_message`. +fn handle_ui_backup_request( + our: &Address, + state: &mut State, + paths: &HashMap<&str, String>, + current_worker_address: &mut Option
, + body: &[u8], +) -> anyhow::Result<()> { + let deserialized = serde_json::from_slice::(body)?; + match deserialized { + // making backup request to server + UiRequest::BackupRequest { + node_id, + password_hash, + .. + } => { + // need password_hash to encrypt data with. necessary for decryption later. + // temporarily storing password_hash, as soon as we get a ServerResponse::Confirm, it's deleted + state.backup_info.data_password_hash = Some(password_hash.clone()); + state.save(); + + let backup_request = serde_json::to_vec(&ClientRequest::BackupRequest { size: 0 })?; + let _ = Request::to(Address::new( + node_id, + ("main", "command_center", "appattacc.os"), + )) + .expects_response(5) + .body(backup_request) + .send(); + } + // client making backup retrieval request to server + UiRequest::BackupRetrieve { node_id } => { + // spawn receiving worker + initialize_worker(our.clone(), current_worker_address)?; + + let backup_retrieve = serde_json::to_vec(&ClientRequest::BackupRetrieve { + worker_address: current_worker_address.clone().unwrap(), + })?; + let _retrieve_backup = Request::to(Address::new( + node_id.clone(), + ("main", "command_center", "appattacc.os"), + )) + .expects_response(5) + .body(backup_retrieve) + .send(); + + // start receiving data on the worker + let _worker_request = Request::new() + .body(serde_json::to_vec( + &WorkerRequest::InitializeReceiverWorker { + receive_to_dir: paths + .get("retrieved_encrypted_backup_path") + .unwrap() + .clone(), + }, + )?) + .target(¤t_worker_address.clone().unwrap()) + .send()?; + } + // decrypt retrieved backup + UiRequest::Decrypt { password_hash, .. } => { + // /command_center:appattacc.os/retrieved_encrypted_backup + // this is the folder where we retrieved the encrypted backup + let dir_entry: DirEntry = DirEntry { + path: paths + .get("retrieved_encrypted_backup_path") + .unwrap() + .clone(), + file_type: FileType::Directory, + }; + + // remove and re-create temp_files_path so it's empty + let request: VfsRequest = VfsRequest { + path: paths.get("temp_files_path").unwrap().clone(), + action: VfsAction::RemoveDirAll, + }; + let _message = Request::new() + .target(("our", "vfs", "distro", "sys")) + .body(serde_json::to_vec(&request)?) + .send_and_await_response(5)?; + let request: VfsRequest = VfsRequest { + path: paths.get("temp_files_path").unwrap().clone(), + action: VfsAction::CreateDirAll, + }; + let _message = Request::new() + .target(("our", "vfs", "distro", "sys")) + .body(serde_json::to_vec(&request)?) + .send_and_await_response(5)?; + + // get all the paths, not content + let dir = read_nested_dir_light(dir_entry)?; + // iterate over all files, and decrypt each one + for path in dir.keys() { + let mut active_file = open_file(path, false, Some(5))?; + let size = active_file.metadata()?.len; + // make sure we start from 0th position every time, + // there were some bugs related to files not being closed, so we would start reading from the previous location + let _pos = active_file.seek(SeekFrom::Start(0))?; + + // the path of each encrypted file looks like so: + // command_center:appattacc.os/retrieved_encrypted_backup/GAXPVM7g...htLlOiu_E3A + let path = Path::new(path); + let file_name = path + .file_name() + .unwrap_or_default() + .to_str() + .unwrap_or_default() + .to_string(); + + // file name decryption + // + // base64/url_safe encoded encrypted file name -> base64 decoded (still encrypted) + // base64 was necessary because of file names not accepting all encrypted chars + let decoded_vec = general_purpose::URL_SAFE.decode(&file_name)?; + // decoded, encrypted file name -> decrypted file name + let decrypted_vec = match decrypt_data(&decoded_vec, password_hash.as_str()) { + Ok(vec) => vec, + Err(e) => { + println!("couldn't decrypt file name"); + return Err(anyhow::anyhow!(e)); + } + }; + let decrypted_path = String::from_utf8(decrypted_vec) + .map_err(|e| anyhow::anyhow!("Failed to convert bytes to string: {}", e))?; + println!("decrypting {}", decrypted_path); + // get full file_path + // one encrypted file name (e.g. q23ewdfvwerv) could be decrypted to a file nested in a folder (e.g. a/b/c/file.md) + let file_path = format!( + "{}/{}", + paths.get("temp_files_path").unwrap().clone(), + decrypted_path + ); + // parent path becomes e.g. a/b/c, separated out from a/b/c/file.md + let parent_path = Path::new(&file_path) + .parent() + .and_then(|p| p.to_str()) + .unwrap_or("") + .to_string(); + // creates nested parent directory (/a/b/c) all the way to the file + let request = VfsRequest { + path: format!("/{}", parent_path).to_string(), + action: VfsAction::CreateDirAll, + }; + let _message = Request::new() + .target(("our", "vfs", "distro", "sys")) + .body(serde_json::to_vec(&request)?) + .send_and_await_response(5)?; + let _dir = open_dir(&parent_path, false, Some(5))?; + + // chunking and decrypting each file + // + // must be decrypted at specific encrypted chunk size. + // encrypted chunk size = chunk size + 44, see files_lib/src/encryption.rs + // + // potential pitfall in the future is if we modify chunk size, + // and try to decrypt at size non corresponding to the size at which it was encrypted. + let num_chunks = (size as f64 / ENCRYPTED_CHUNK_SIZE as f64).ceil() as u64; + + // iterate over encrypted file + for i in 0..num_chunks { + let offset = i * ENCRYPTED_CHUNK_SIZE; + let length = ENCRYPTED_CHUNK_SIZE.min(size - offset); // size=file size + let mut buffer = vec![0; length as usize]; + let _pos = active_file.seek(SeekFrom::Current(0))?; + active_file.read_at(&mut buffer)?; + + // decrypt data with password_hash + let decrypted_bytes = match decrypt_data(&buffer, password_hash.as_str()) { + Ok(vec) => vec, + Err(_e) => { + println!("couldn't decrypt file data"); + return Err(anyhow::anyhow!("couldn't decrypt file data")); + } + }; + + let dir = open_dir(&parent_path, false, None)?; + + // there is an issue with open_file(create: true), so we have to do it manually + let entries = dir.read()?; + if entries.contains(&DirEntry { + path: file_path[1..].to_string(), + file_type: FileType::File, + }) { + } else { + let _file = create_file(&file_path, Some(5))?; + } + + let mut file = open_file(&file_path, false, Some(5))?; + file.append(&decrypted_bytes)?; + } + } + // after all decryption is successful, the files are stored in the files_temp folder. + // we remove the files folder (where our current notes are stored), and rename files_temp to files, + // effectively overwriting files. + let request: VfsRequest = VfsRequest { + path: paths.get("our_files_path").unwrap().clone(), + action: VfsAction::RemoveDirAll, + }; + let _message = Request::new() + .target(("our", "vfs", "distro", "sys")) + .body(serde_json::to_vec(&request)?) + .send_and_await_response(5)?; + let request: VfsRequest = VfsRequest { + path: paths.get("temp_files_path").unwrap().clone(), + action: VfsAction::Rename { + new_path: paths.get("our_files_path").unwrap().clone().to_string(), + }, + }; + let _message = Request::new() + .target(("our", "vfs", "distro", "sys")) + .body(serde_json::to_vec(&request)?) + .send_and_await_response(5)?; + // create empty files_temp for future use + let _ = create_drive(our.package_id(), "files_temp", Some(5)); + } + } + println!("decryption done"); + Ok(()) +} + +// handles backup related messages from another node +fn handle_backup_message( + our: &Address, + state: &mut State, + paths: &HashMap<&str, String>, + current_worker_address: &mut Option
, + message: &Message, +) -> anyhow::Result<()> { + println!("HERE"); + match &message { + Message::Request { body, .. } => { + println!("HERE1"); + + let deserialized: ClientRequest = serde_json::from_slice::(body)?; + println!("HERE2"); + + match deserialized { + // server receiving backup request from client + ClientRequest::BackupRequest { size } => { + println!("HERE3"); + + // TODO: add criterion here + // whether we want to provide backup or not. + // currently responds with Confirm, should respond with Confirm or Decline based on a setting + + state + .backup_info + .backups_time_map + .insert(message.source().node.to_string(), chrono::Utc::now()); + state.save(); + println!("HERE4"); + + initialize_worker(our.clone(), current_worker_address)?; + + let backup_response: Vec = serde_json::to_vec( + &ServerResponse::BackupRequestResponse(BackupRequestResponse::Confirm { + worker_address: current_worker_address.clone().unwrap(), + }), + )?; + let _resp: Result<(), anyhow::Error> = + Response::new().body(backup_response).send(); + println!("HERE5"); + + // telling the worker to start receiving the backup + let _worker_request = Request::new() + .body(serde_json::to_vec( + &WorkerRequest::InitializeReceiverWorker { + receive_to_dir: format!( + "{}/{}", + paths.get("encrypted_storage_path").unwrap().clone(), + message.source().node.clone() + ), + }, + )?) + .target(¤t_worker_address.clone().unwrap()) + .send()?; + println!("HERE6"); + } + // server receiving backup retrieval request from client + ClientRequest::BackupRetrieve { worker_address } => { + initialize_worker(our.clone(), current_worker_address)?; + + // telling the worker to start sending the encrypted backup to the client + let _worker_request = Request::new() + .body(serde_json::to_vec( + &WorkerRequest::InitializeSenderWorker { + target_worker: worker_address.clone(), + password_hash: None, + sending_from_dir: format!( + "{}/{}", + paths.get("encrypted_storage_path").unwrap(), + worker_address.node() + ), + }, + )?) + .target(¤t_worker_address.clone().unwrap()) + .send()?; + + // telling the client what time the backup was from + let backup_response: Vec = + serde_json::to_vec(&ServerResponse::BackupRetrieveResponse( + state + .backup_info + .backups_time_map + .get(&message.source().node) + .copied(), + ))?; + let _resp: Result<(), anyhow::Error> = + Response::new().body(backup_response).send(); + } + } + } + // receiving backup response from server + Message::Response { body, .. } => { + let deserialized: ServerResponse = serde_json::from_slice::(body)?; + match deserialized { + ServerResponse::BackupRetrieveResponse(datetime) => { + state.backup_info.notes_last_backed_up_at = datetime; + state.save() + } + ServerResponse::BackupRequestResponse(backup_response) => match backup_response { + BackupRequestResponse::Confirm { worker_address } => { + println!( + "received Confirm backup_response from {:?}", + message.source().node, + ); + + initialize_worker(our.clone(), current_worker_address)?; + + // telling the worker to start sending the backup + let _worker_request = Request::new() + .body(serde_json::to_vec( + &WorkerRequest::InitializeSenderWorker { + target_worker: worker_address, + password_hash: state.backup_info.data_password_hash.clone(), + sending_from_dir: paths.get("our_files_path").unwrap().clone(), + }, + )?) + .target(¤t_worker_address.clone().unwrap()) + .send()?; + + state.backup_info.notes_last_backed_up_at = Some(chrono::Utc::now()); + state.backup_info.notes_backup_provider = + Some(message.source().node.clone()); + // we dont need to store password hash any more + state.backup_info.data_password_hash = None; + state.save(); + } + BackupRequestResponse::Decline { .. } => { + println!( + "received Decline backup_response from {:?}", + message.source().node, + ); + } + }, + } + } + } + return Ok(()); +} + +// handles requests from the ui +fn handle_http_request( + our: &Address, + state: &mut State, + ws_channel_id: &mut Option, + pkgs: &HashMap, + paths: &HashMap<&str, String>, + current_worker_address: &mut Option
, + body: &[u8], +) -> anyhow::Result<()> { + let http_request = http::HttpServerRequest::from_bytes(body)?; + + if let http::HttpServerRequest::WebSocketOpen { channel_id, .. } = http_request { + *ws_channel_id = Some(channel_id); + return Ok(()); + } + + let http_request = http_request + .request() + .ok_or_else(|| anyhow::anyhow!("Failed to parse http request"))?; + let path = http_request.path()?; + let bytes = get_blob() + .ok_or_else(|| anyhow::anyhow!("Failed to get blob"))? + .bytes; + match path.as_str() { + "/fetch_api_keys" => fetch_api_keys(state), + "/fetch_backup_data" => fetch_backup_data(state), + "/submit_api_keys" => submit_api_keys(state, pkgs, &bytes), + "/fetch_notes" => fetch_notes(paths.get("our_files_path").unwrap()), + "/import_notes" => import_notes_from_ui(&bytes, paths.get("our_files_path").unwrap()), + "/backup_request" => { + // WIP, should take BackupRequest, BackupRetrieve, and Decrypt + // (or decrypt should be done automatically?) + println!("got /backup_request"); + let deserialized: Result = serde_json::from_slice(&bytes); + match deserialized { + Ok(value) => { + println!("Deserialized backup request: {:?}", value); + let _ = handle_ui_backup_request(our, state, paths, current_worker_address, &bytes); + Ok(()) + } + Err(e) => { + println!("Error deserializing backup request: {:?}", e); + println!("Received bytes: {:?}", String::from_utf8_lossy(&bytes)); + Err(anyhow::anyhow!( + "Failed to deserialize backup request: {}", + e + )) + } + } + } + _ => Ok(()), + } +} + +fn handle_http_message( + our: &Address, + state: &mut State, + ws_channel_id: &mut Option, + pkgs: &HashMap, + paths: &HashMap<&str, String>, + current_worker_address: &mut Option
, + message: &Message, +) -> anyhow::Result<()> { + match message { + Message::Request { ref body, .. } => { + handle_http_request(our, state, ws_channel_id, pkgs, paths, current_worker_address, body) + } + Message::Response { .. } => Ok(()), + } +} + +fn handle_message( + our: &Address, + state: &mut State, + ws_channel_id: &mut Option, + pkgs: &HashMap, + paths: &HashMap<&str, String>, + current_worker_address: &mut Option
, +) -> anyhow::Result<()> { + let message = await_message()?; + + if message.source().node != our.node { + handle_backup_message(our, state, paths, current_worker_address, &message)?; + } + + if let "http_server:distro:sys" | "http_client:distro:sys" = + message.source().process.to_string().as_str() + { + return handle_http_message(our, state, ws_channel_id, pkgs, paths, current_worker_address, &message); + } + + // current worker finishing up + if let Some(worker_address) = current_worker_address { + if worker_address == message.source() { + match serde_json::from_slice(&message.body())? { + WorkerStatus::Done => { + *current_worker_address = None; + + let blob = LazyLoadBlob { + mime: Some("application/json".to_string()), + bytes: serde_json::json!({ + "WorkerStatus": WorkerStatus::Done + }) + .to_string() + .as_bytes() + .to_vec(), + }; + + send_ws_push(ws_channel_id.unwrap_or(0), WsMessageType::Text, blob); + + return Ok(()); + } + } + } + } + + // helper for debugging. remove for prod. + // it takes inputs from the teriminal + handle_ui_backup_request(our, state, paths, current_worker_address, &message.body()) +} + +const ICON: &str = include_str!("icon"); +call_init!(init); +fn init(our: Address) { + let mut ws_channel_id: Option = None; + bind_ws_path("/", true, false).unwrap(); + + let _ = http::serve_ui( + &our, + "ui", + true, + false, + vec![ + "/", + "/submit_api_keys", + "/fetch_api_keys", + "/fetch_notes", + "/import_notes", + "/backup_request", + "/fetch_backup_data", + ], + ); + + // add ourselves to the homepage + Request::to(("our", "homepage", "homepage", "sys")) + .body( + serde_json::json!({ + "Add": { + "label": "Command Center", + "icon": ICON, + "path": "/", // just our root + } + }) + .to_string() + .as_bytes() + .to_vec(), + ) + .send() + .unwrap(); + + let mut state = State::fetch() + .unwrap_or_else(|| State::new(&our, ApiKeys::default(), BackupInfo::default())); + + let mut pkgs = HashMap::new(); + pkgs.insert( + Pkg::LLM, + Address::new(&our.node, ("openai", "command_center", "appattacc.os")), + ); + pkgs.insert( + Pkg::STT, + Address::new( + &our.node, + ("speech_to_text", "command_center", "appattacc.os"), + ), + ); + pkgs.insert( + Pkg::Telegram, + Address::new(&our.node, ("tg", "command_center", "appattacc.os")), + ); + + // calling RegisterApiKey because it calls getUpdates (necessary every time a process is restarted) + if let Some(telegram_key) = &state.api_keys.telegram_key { + let init = TgInitialize { + token: telegram_key.clone(), + params: None, + }; + let req = serde_json::to_vec(&TgRequest::RegisterApiKey(init)); + let _ = Request::new() + .target(pkgs.get(&Pkg::Telegram).unwrap()) + .body(req.unwrap()) + .send_and_await_response(5); + } + + let temp_files_path = create_drive(our.package_id(), "files_temp", Some(5)).unwrap(); + let our_files_path = create_drive(our.package_id(), "files", Some(5)).unwrap(); + let encrypted_storage_path = + create_drive(our.package_id(), "encrypted_storage", Some(5)).unwrap(); + let retrieved_encrypted_backup_path = + create_drive(our.package_id(), "retrieved_encrypted_backup", Some(5)).unwrap(); + let mut paths = HashMap::new(); + paths.insert("our_files_path", our_files_path); + paths.insert("temp_files_path", temp_files_path); + paths.insert( + "retrieved_encrypted_backup_path", + retrieved_encrypted_backup_path, + ); + paths.insert("encrypted_storage_path", encrypted_storage_path); + + let mut current_worker_address: Option
= None; + + loop { + match handle_message(&our, &mut state, &mut ws_channel_id, &pkgs, &paths, &mut current_worker_address) { + Ok(_) => {} + Err(e) => println!("Error: {:?}", e), + } + } +} diff --git a/command_center/command_center/src/structs.rs b/command_center/command_center/src/structs.rs new file mode 100644 index 00000000..7b84d3d1 --- /dev/null +++ b/command_center/command_center/src/structs.rs @@ -0,0 +1,61 @@ +use chrono::{DateTime, Utc}; +use kinode_process_lib::NodeId; +use kinode_process_lib::{get_state, set_state, Address}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)] +pub enum Pkg { + LLM, + STT, + Telegram, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct State { + pub our: Address, + pub api_keys: ApiKeys, + pub backup_info: BackupInfo, +} + +impl State { + pub fn new(our: &Address, api_keys: ApiKeys, backup_info: BackupInfo) -> Self { + State { + our: our.clone(), + api_keys, + backup_info + } + } + + pub fn fetch() -> Option { + if let Some(state_bytes) = get_state() { + bincode::deserialize(&state_bytes).ok() + } else { + None + } + } + + pub fn save(&self) { + let serialized_state = bincode::serialize(self).expect("Failed to serialize state"); + set_state(&serialized_state); + } +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct ApiKeys { + pub telegram_key: Option, + pub openai_key: Option, + pub groq_key: Option, +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct BackupInfo { + // temporarily stores the password hash for encryption and decryption purposes + pub data_password_hash: Option, + // used by backup provider to track which nodes it is providing backups to + pub backups_time_map: HashMap>, + // when we last backed up our notes + pub notes_last_backed_up_at: Option>, + // our backup provider + pub notes_backup_provider: Option, +} diff --git a/command_center/src/tg_api.rs b/command_center/command_center/src/tg_api.rs similarity index 100% rename from command_center/src/tg_api.rs rename to command_center/command_center/src/tg_api.rs diff --git a/command_center/metadata.json b/command_center/metadata.json new file mode 100644 index 00000000..ea8954a2 --- /dev/null +++ b/command_center/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Command Center", + "description": "Api Aggregator", + "image": "https://i.imgur.com/B3c5h4k.png", + "properties": { + "package_name": "command_center", + "current_version": "0.1.5", + "publisher": "appattacc.os", + "mirrors": [ + "appattacc.os" + ], + "code_hashes": { + "0.1.5": "cee6d91e9f8702e07bbf0e942e4b3dc037563d0f2dd5fa9c4088b2c81e336b5b" + }, + "wit_version": 0, + "dependencies": [] + }, + "external_url": "https://github.com/kinode-dao/command_center", + "animation_url": "" +} diff --git a/command_center/package-lock.json b/command_center/package-lock.json new file mode 100644 index 00000000..74fc8b80 --- /dev/null +++ b/command_center/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "command_center", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/command_center/pkg/ai_chatbot_demo.wasm b/command_center/pkg/ai_chatbot_demo.wasm new file mode 100644 index 00000000..fe9f3811 Binary files /dev/null and b/command_center/pkg/ai_chatbot_demo.wasm differ diff --git a/command_center/pkg/api.zip b/command_center/pkg/api.zip new file mode 100644 index 00000000..15cb0ecb Binary files /dev/null and b/command_center/pkg/api.zip differ diff --git a/command_center/pkg/command_center.wasm b/command_center/pkg/command_center.wasm new file mode 100644 index 00000000..a3f52dcc Binary files /dev/null and b/command_center/pkg/command_center.wasm differ diff --git a/command_center/pkg/manifest.json b/command_center/pkg/manifest.json new file mode 100644 index 00000000..fdc00856 --- /dev/null +++ b/command_center/pkg/manifest.json @@ -0,0 +1,96 @@ +[ + { + "process_name": "main", + "process_wasm_path": "/command_center.wasm", + "on_exit": "Restart", + "request_networking": true, + "request_capabilities": [ + "http_server:distro:sys", + "http_client:distro:sys", + "openai:llm:kinode", + "vfs:distro:sys", + "homepage:homepage:sys", + "sqlite:distro:sys" + ], + "grant_capabilities": [ + "http_server:distro:sys", + "http_client:distro:sys", + "openai:command_center:appattacc.os", + "speech_to_text:command_center:appattacc.os", + "tg:command_center:appattacc.os", + "sqlite:distro:sys", + "terminal:terminal:sys" + ], + "public": false + }, + { + "process_name": "tg", + "process_wasm_path": "/tg.wasm", + "on_exit": "Restart", + "request_networking": true, + "request_capabilities": [ + "http_server:distro:sys", + "http_client:distro:sys", + "net:distro:sys", + "sqlite:distro:sys" + ], + "grant_capabilities": [ + "main:command_center:appattacc.os", + "http_server:distro:sys", + "http_client:distro:sys", + "sqlite:distro:sys" + ], + "public": false + }, + { + "process_name": "speech_to_text", + "process_wasm_path": "/speech_to_text.wasm", + "on_exit": "Restart", + "request_networking": true, + "request_capabilities": [ + "http_server:distro:sys", + "http_client:distro:sys" + ], + "grant_capabilities": [ + "main:command_center:appattacc.os", + "http_server:distro:sys", + "http_client:distro:sys" + ], + "public": false + }, + { + "process_name": "openai", + "process_wasm_path": "/openai.wasm", + "on_exit": "Restart", + "request_networking": true, + "request_capabilities": [ + "http_client:distro:sys" + ], + "grant_capabilities": [ + "http_client:distro:sys", + "http_server:distro:sys", + "main:command_center:appattacc.os" + ], + "public": true + }, + { + "process_name": "ai_chatbot_demo", + "process_wasm_path": "/ai_chatbot_demo.wasm", + "on_exit": "None", + "request_networking": true, + "request_capabilities": [ + "http_server:distro:sys", + "main:command_center:appattacc.os", + "tg:command_center:appattacc.os", + "speech_to_text:command_center:appattacc.os", + "openai:command_center:appattacc.os" + ], + "grant_capabilities": [ + "main:command_center:appattacc.os", + "tg:command_center:appattacc.os", + "speech_to_text:command_center:appattacc.os", + "openai:command_center:appattacc.os" + ], + "public": false + } +] \ No newline at end of file diff --git a/command_center/pkg/openai.wasm b/command_center/pkg/openai.wasm new file mode 100644 index 00000000..09a34fd1 Binary files /dev/null and b/command_center/pkg/openai.wasm differ diff --git a/pkg/scripts.json b/command_center/pkg/scripts.json similarity index 100% rename from pkg/scripts.json rename to command_center/pkg/scripts.json diff --git a/command_center/pkg/speech_to_text.wasm b/command_center/pkg/speech_to_text.wasm new file mode 100644 index 00000000..513a7c73 Binary files /dev/null and b/command_center/pkg/speech_to_text.wasm differ diff --git a/command_center/pkg/tg.wasm b/command_center/pkg/tg.wasm new file mode 100644 index 00000000..0dc6209d Binary files /dev/null and b/command_center/pkg/tg.wasm differ diff --git a/command_center/pkg/worker.wasm b/command_center/pkg/worker.wasm new file mode 100644 index 00000000..ee2fc7cf Binary files /dev/null and b/command_center/pkg/worker.wasm differ diff --git a/command_center/src/lib.rs b/command_center/src/lib.rs deleted file mode 100644 index db473354..00000000 --- a/command_center/src/lib.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::collections::HashMap; - -use kinode_process_lib::{ - await_message, call_init, get_blob, http, println, Address, Message, Request, -}; -use llm_interface::openai::*; -use stt_interface::*; -use telegram_interface::*; - -mod structs; -use structs::*; - -mod tg_api; - -mod spawners; -use spawners::*; - -wit_bindgen::generate!({ - path: "wit", - world: "process", -}); - -fn handle_http_message( - our: &Address, - message: &Message, - state: &mut Option, - pkgs: &HashMap, -) -> anyhow::Result<()> { - match message { - Message::Request { ref body, .. } => handle_http_request(our, state, body, pkgs), - Message::Response { .. } => Ok(()), - } -} - -fn handle_http_request( - our: &Address, - state: &mut Option, - body: &[u8], - pkgs: &HashMap, -) -> anyhow::Result<()> { - let http_request = http::HttpServerRequest::from_bytes(body)? - .request() - .ok_or_else(|| anyhow::anyhow!("Failed to parse http request"))?; - let path = http_request.path()?; - let bytes = get_blob() - .ok_or_else(|| anyhow::anyhow!("Failed to get blob"))? - .bytes; - - match path.as_str() { - "/status" => fetch_status(), - "/submit_config" => submit_config(our, &bytes, state, pkgs), - _ => Ok(()), - } -} - -fn fetch_status() -> anyhow::Result<()> { - let state = State::fetch().ok_or_else(|| anyhow::anyhow!("State being fetched for the first time (or failed)"))?; - let config = &state.config; - let response_body = serde_json::to_string(&config)?; - http::send_response( - http::StatusCode::OK, - Some(HashMap::from([( - "Content-Type".to_string(), - "application/json".to_string(), - )])), - response_body.as_bytes().to_vec(), - ); - Ok(()) -} - -fn submit_config( - our: &Address, - body_bytes: &[u8], - state: &mut Option, - pkgs: &HashMap, -) -> anyhow::Result<()> { - let initial_config = serde_json::from_slice::(body_bytes)?; - match state { - Some(state_) => { - println!("Modifying state to {:?}", initial_config); - state_.config = initial_config; - } - None => { - println!("Creating state {:?}", initial_config); - *state = Some(State::new(our, initial_config)); - } - } - - if let Some(ref mut state) = state { - for (pkg, addr) in pkgs.iter() { - match pkg { - Pkg::LLM => { - if let Some(openai_key) = &state.config.openai_key { - let req = serde_json::to_vec(&LLMRequest::RegisterOpenaiApiKey( - RegisterApiKeyRequest { - api_key: openai_key.clone(), - }, - ))?; - let _ = Request::new() - .target(addr.clone()) - .body(req) - .send_and_await_response(5)??; - } - if let Some(groq_key) = &state.config.groq_key { - let req = serde_json::to_vec( - &llm_interface::openai::LLMRequest::RegisterGroqApiKey( - RegisterApiKeyRequest { - api_key: groq_key.clone(), - }, - ), - )?; - let _ = Request::new() - .target(addr.clone()) - .body(req) - .send_and_await_response(5)??; - } - } - Pkg::STT => { - if let Some(openai_key) = &state.config.openai_key { - let req = - serde_json::to_vec(&STTRequest::RegisterApiKey(openai_key.clone()))?; - let _ = Request::new() - .target(addr.clone()) - .body(req) - .send_and_await_response(5)??; - } - } - Pkg::Telegram => { - if let Some(telegram_key) = &state.config.telegram_key { - let init = TgInitialize { - token: telegram_key.clone(), - params: None, - }; - let req = serde_json::to_vec(&TgRequest::RegisterApiKey(init))?; - let _ = Request::new() - .target(addr.clone()) - .body(req) - .send_and_await_response(5)??; - } - } - } - } - state.save(); - - http::send_response( - http::StatusCode::OK, - Some(HashMap::from([( - "Content-Type".to_string(), - "application/json".to_string(), - )])), - b"{\"message\": \"success\"}".to_vec(), - ); - } - Ok(()) -} - -fn handle_message( - our: &Address, - state: &mut Option, - pkgs: &HashMap, -) -> anyhow::Result<()> { - let message = await_message()?; - if message.source().node != our.node { - return Ok(()); - } - - match message.source().process.to_string().as_str() { - "http_server:distro:sys" | "http_client:distro:sys" => { - handle_http_message(&our, &message, state, pkgs) - } - _ => Ok(()), - } -} - -const ICON: &str = include_str!("icon"); -call_init!(init); -fn init(our: Address) { - let _ = http::serve_ui( - &our, - "ui", - true, - false, - vec!["/", "/submit_config", "/status"], - ); - let mut state = State::fetch(); - - // add ourselves to the homepage - Request::to(("our", "homepage", "homepage", "sys")) - .body( - serde_json::json!({ - "Add": { - "label": "Command Center", - "icon": ICON, - "path": "/", // just our root - } - }) - .to_string() - .as_bytes() - .to_vec(), - ) - .send() - .unwrap(); - - let Ok(pkgs) = spawners::spawn_pkgs(&our) else { - panic!("Failed to spawn pkgs"); - }; - - loop { - match handle_message(&our, &mut state, &pkgs) { - Ok(_) => {} - Err(e) => println!("Error: {:?}", e), - } - } -} diff --git a/command_center/src/spawners.rs b/command_center/src/spawners.rs deleted file mode 100644 index 37508d7f..00000000 --- a/command_center/src/spawners.rs +++ /dev/null @@ -1,40 +0,0 @@ -use kinode_process_lib::{Address, ProcessId, OnExit, spawn, our_capabilities}; -use std::str::FromStr; -use std::collections::HashMap; -use serde::{Serialize, Deserialize}; - -#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)] -pub enum Pkg { - LLM, - STT, - Telegram, -} - -pub fn spawn_pkgs(our: &Address) -> anyhow::Result> { - let mut addresses = HashMap::new(); - addresses.insert(Pkg::LLM, spawn_pkg(our, "openai.wasm")?); - addresses.insert(Pkg::STT, spawn_pkg(our, "speech_to_text.wasm")?); - addresses.insert(Pkg::Telegram, spawn_pkg(our, "tg.wasm")?); - Ok(addresses) -} - -fn spawn_pkg(our: &Address, pkg_name: &str) -> anyhow::Result
{ - let name = pkg_name.split('.').next(); - let pkg_path = format!("{}/pkg/{}", our.package_id(), pkg_name); - let our_caps = our_capabilities(); - let http_client = ProcessId::from_str("http_client:distro:sys").unwrap(); - - let process_id = spawn( - name, - &pkg_path, - OnExit::None, - our_caps, - vec![http_client], - false, - )?; - - Ok(Address { - node: our.node.clone(), - process: process_id, - }) -} \ No newline at end of file diff --git a/command_center/src/structs.rs b/command_center/src/structs.rs deleted file mode 100644 index 3005ce2a..00000000 --- a/command_center/src/structs.rs +++ /dev/null @@ -1,38 +0,0 @@ -use serde::{Deserialize, Serialize}; -use kinode_process_lib::{get_state, set_state, Address}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct State { - pub our: Address, - pub config: InitialConfig, -} - -impl State { - pub fn new(our: &Address, config: InitialConfig) -> Self { - State { - our: our.clone(), - config, - } - } - - pub fn fetch() -> Option { - if let Some(state_bytes) = get_state() { - bincode::deserialize(&state_bytes).ok() - } else { - None - } - } - - pub fn save(&self) { - let serialized_state = bincode::serialize(self).expect("Failed to serialize state"); - set_state(&serialized_state); - } -} - -#[derive(Serialize, Deserialize, Default, Debug, Clone)] -pub struct InitialConfig { - pub telegram_key: Option, - pub openai_key: Option, - pub groq_key: Option, -} - diff --git a/command_center/ui/.eslintrc.cjs b/command_center/ui/.eslintrc.cjs new file mode 100644 index 00000000..3e212e1d --- /dev/null +++ b/command_center/ui/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react/jsx-no-target-blank': 'off', + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/command_center/ui/.gitignore b/command_center/ui/.gitignore new file mode 100644 index 00000000..8694df3c --- /dev/null +++ b/command_center/ui/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +yarn.lock \ No newline at end of file diff --git a/command_center/ui/.vite/deps/@uiw_react-md-editor.js b/command_center/ui/.vite/deps/@uiw_react-md-editor.js new file mode 100644 index 00000000..2148f3f9 --- /dev/null +++ b/command_center/ui/.vite/deps/@uiw_react-md-editor.js @@ -0,0 +1,53023 @@ +import { + _extends, + _objectWithoutPropertiesLoose, + require_jsx_runtime +} from "./chunk-BON3UWJ6.js"; +import { + __commonJS, + __export, + __publicField, + __toESM, + require_react +} from "./chunk-DDNM7ENY.js"; + +// node_modules/parse-numeric-range/index.js +var require_parse_numeric_range = __commonJS({ + "node_modules/parse-numeric-range/index.js"(exports, module) { + function parsePart(string3) { + let res = []; + let m; + for (let str of string3.split(",").map((str2) => str2.trim())) { + if (/^-?\d+$/.test(str)) { + res.push(parseInt(str, 10)); + } else if (m = str.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)) { + let [_2, lhs, sep, rhs] = m; + if (lhs && rhs) { + lhs = parseInt(lhs); + rhs = parseInt(rhs); + const incr = lhs < rhs ? 1 : -1; + if (sep === "-" || sep === ".." || sep === "‥") rhs += incr; + for (let i = lhs; i !== rhs; i += incr) res.push(i); + } + } + } + return res; + } + exports.default = parsePart; + module.exports = parsePart; + } +}); + +// node_modules/boolbase/index.js +var require_boolbase = __commonJS({ + "node_modules/boolbase/index.js"(exports, module) { + module.exports = { + trueFunc: function trueFunc() { + return true; + }, + falseFunc: function falseFunc() { + return false; + } + }; + } +}); + +// node_modules/inline-style-parser/index.js +var require_inline_style_parser = __commonJS({ + "node_modules/inline-style-parser/index.js"(exports, module) { + var COMMENT_REGEX = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g; + var NEWLINE_REGEX = /\n/g; + var WHITESPACE_REGEX = /^\s*/; + var PROPERTY_REGEX = /^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/; + var COLON_REGEX = /^:\s*/; + var VALUE_REGEX = /^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/; + var SEMICOLON_REGEX = /^[;\s]*/; + var TRIM_REGEX = /^\s+|\s+$/g; + var NEWLINE = "\n"; + var FORWARD_SLASH = "/"; + var ASTERISK = "*"; + var EMPTY_STRING = ""; + var TYPE_COMMENT = "comment"; + var TYPE_DECLARATION = "declaration"; + module.exports = function(style3, options) { + if (typeof style3 !== "string") { + throw new TypeError("First argument must be a string"); + } + if (!style3) return []; + options = options || {}; + var lineno = 1; + var column = 1; + function updatePosition(str) { + var lines = str.match(NEWLINE_REGEX); + if (lines) lineno += lines.length; + var i = str.lastIndexOf(NEWLINE); + column = ~i ? str.length - i : column + str.length; + } + function position4() { + var start2 = { line: lineno, column }; + return function(node2) { + node2.position = new Position(start2); + whitespace3(); + return node2; + }; + } + function Position(start2) { + this.start = start2; + this.end = { line: lineno, column }; + this.source = options.source; + } + Position.prototype.content = style3; + var errorsList = []; + function error(msg) { + var err = new Error( + options.source + ":" + lineno + ":" + column + ": " + msg + ); + err.reason = msg; + err.filename = options.source; + err.line = lineno; + err.column = column; + err.source = style3; + if (options.silent) { + errorsList.push(err); + } else { + throw err; + } + } + function match(re2) { + var m = re2.exec(style3); + if (!m) return; + var str = m[0]; + updatePosition(str); + style3 = style3.slice(str.length); + return m; + } + function whitespace3() { + match(WHITESPACE_REGEX); + } + function comments(rules) { + var c3; + rules = rules || []; + while (c3 = comment5()) { + if (c3 !== false) { + rules.push(c3); + } + } + return rules; + } + function comment5() { + var pos = position4(); + if (FORWARD_SLASH != style3.charAt(0) || ASTERISK != style3.charAt(1)) return; + var i = 2; + while (EMPTY_STRING != style3.charAt(i) && (ASTERISK != style3.charAt(i) || FORWARD_SLASH != style3.charAt(i + 1))) { + ++i; + } + i += 2; + if (EMPTY_STRING === style3.charAt(i - 1)) { + return error("End of comment missing"); + } + var str = style3.slice(2, i - 2); + column += 2; + updatePosition(str); + style3 = style3.slice(i); + column += 2; + return pos({ + type: TYPE_COMMENT, + comment: str + }); + } + function declaration() { + var pos = position4(); + var prop = match(PROPERTY_REGEX); + if (!prop) return; + comment5(); + if (!match(COLON_REGEX)) return error("property missing ':'"); + var val = match(VALUE_REGEX); + var ret = pos({ + type: TYPE_DECLARATION, + property: trim(prop[0].replace(COMMENT_REGEX, EMPTY_STRING)), + value: val ? trim(val[0].replace(COMMENT_REGEX, EMPTY_STRING)) : EMPTY_STRING + }); + match(SEMICOLON_REGEX); + return ret; + } + function declarations() { + var decls = []; + comments(decls); + var decl; + while (decl = declaration()) { + if (decl !== false) { + decls.push(decl); + comments(decls); + } + } + return decls; + } + whitespace3(); + return declarations(); + }; + function trim(str) { + return str ? str.replace(TRIM_REGEX, EMPTY_STRING) : EMPTY_STRING; + } + } +}); + +// node_modules/style-to-object/cjs/index.js +var require_cjs = __commonJS({ + "node_modules/style-to-object/cjs/index.js"(exports) { + "use strict"; + var __importDefault = exports && exports.__importDefault || function(mod) { + return mod && mod.__esModule ? mod : { "default": mod }; + }; + Object.defineProperty(exports, "__esModule", { value: true }); + var inline_style_parser_1 = __importDefault(require_inline_style_parser()); + function StyleToObject2(style3, iterator) { + var styleObject = null; + if (!style3 || typeof style3 !== "string") { + return styleObject; + } + var declarations = (0, inline_style_parser_1.default)(style3); + var hasIterator = typeof iterator === "function"; + declarations.forEach(function(declaration) { + if (declaration.type !== "declaration") { + return; + } + var property = declaration.property, value = declaration.value; + if (hasIterator) { + iterator(property, value, declaration); + } else if (value) { + styleObject = styleObject || {}; + styleObject[property] = value; + } + }); + return styleObject; + } + exports.default = StyleToObject2; + } +}); + +// node_modules/ms/index.js +var require_ms = __commonJS({ + "node_modules/ms/index.js"(exports, module) { + var s4 = 1e3; + var m = s4 * 60; + var h3 = m * 60; + var d2 = h3 * 24; + var w = d2 * 7; + var y = d2 * 365.25; + module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === "string" && val.length > 0) { + return parse7(val); + } else if (type === "number" && isFinite(val)) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + "val is not a non-empty string or a valid number. val=" + JSON.stringify(val) + ); + }; + function parse7(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n2 = parseFloat(match[1]); + var type = (match[2] || "ms").toLowerCase(); + switch (type) { + case "years": + case "year": + case "yrs": + case "yr": + case "y": + return n2 * y; + case "weeks": + case "week": + case "w": + return n2 * w; + case "days": + case "day": + case "d": + return n2 * d2; + case "hours": + case "hour": + case "hrs": + case "hr": + case "h": + return n2 * h3; + case "minutes": + case "minute": + case "mins": + case "min": + case "m": + return n2 * m; + case "seconds": + case "second": + case "secs": + case "sec": + case "s": + return n2 * s4; + case "milliseconds": + case "millisecond": + case "msecs": + case "msec": + case "ms": + return n2; + default: + return void 0; + } + } + function fmtShort(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d2) { + return Math.round(ms / d2) + "d"; + } + if (msAbs >= h3) { + return Math.round(ms / h3) + "h"; + } + if (msAbs >= m) { + return Math.round(ms / m) + "m"; + } + if (msAbs >= s4) { + return Math.round(ms / s4) + "s"; + } + return ms + "ms"; + } + function fmtLong(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d2) { + return plural(ms, msAbs, d2, "day"); + } + if (msAbs >= h3) { + return plural(ms, msAbs, h3, "hour"); + } + if (msAbs >= m) { + return plural(ms, msAbs, m, "minute"); + } + if (msAbs >= s4) { + return plural(ms, msAbs, s4, "second"); + } + return ms + " ms"; + } + function plural(ms, msAbs, n2, name3) { + var isPlural = msAbs >= n2 * 1.5; + return Math.round(ms / n2) + " " + name3 + (isPlural ? "s" : ""); + } + } +}); + +// node_modules/debug/src/common.js +var require_common = __commonJS({ + "node_modules/debug/src/common.js"(exports, module) { + function setup(env2) { + createDebug2.debug = createDebug2; + createDebug2.default = createDebug2; + createDebug2.coerce = coerce; + createDebug2.disable = disable2; + createDebug2.enable = enable; + createDebug2.enabled = enabled2; + createDebug2.humanize = require_ms(); + createDebug2.destroy = destroy; + Object.keys(env2).forEach((key2) => { + createDebug2[key2] = env2[key2]; + }); + createDebug2.names = []; + createDebug2.skips = []; + createDebug2.formatters = {}; + function selectColor(namespace) { + let hash = 0; + for (let i = 0; i < namespace.length; i++) { + hash = (hash << 5) - hash + namespace.charCodeAt(i); + hash |= 0; + } + return createDebug2.colors[Math.abs(hash) % createDebug2.colors.length]; + } + createDebug2.selectColor = selectColor; + function createDebug2(namespace) { + let prevTime; + let enableOverride = null; + let namespacesCache; + let enabledCache; + function debug2(...args) { + if (!debug2.enabled) { + return; + } + const self2 = debug2; + const curr = Number(/* @__PURE__ */ new Date()); + const ms = curr - (prevTime || curr); + self2.diff = ms; + self2.prev = prevTime; + self2.curr = curr; + prevTime = curr; + args[0] = createDebug2.coerce(args[0]); + if (typeof args[0] !== "string") { + args.unshift("%O"); + } + let index2 = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { + if (match === "%%") { + return "%"; + } + index2++; + const formatter = createDebug2.formatters[format]; + if (typeof formatter === "function") { + const val = args[index2]; + match = formatter.call(self2, val); + args.splice(index2, 1); + index2--; + } + return match; + }); + createDebug2.formatArgs.call(self2, args); + const logFn = self2.log || createDebug2.log; + logFn.apply(self2, args); + } + debug2.namespace = namespace; + debug2.useColors = createDebug2.useColors(); + debug2.color = createDebug2.selectColor(namespace); + debug2.extend = extend2; + debug2.destroy = createDebug2.destroy; + Object.defineProperty(debug2, "enabled", { + enumerable: true, + configurable: false, + get: () => { + if (enableOverride !== null) { + return enableOverride; + } + if (namespacesCache !== createDebug2.namespaces) { + namespacesCache = createDebug2.namespaces; + enabledCache = createDebug2.enabled(namespace); + } + return enabledCache; + }, + set: (v2) => { + enableOverride = v2; + } + }); + if (typeof createDebug2.init === "function") { + createDebug2.init(debug2); + } + return debug2; + } + function extend2(namespace, delimiter) { + const newDebug = createDebug2(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace); + newDebug.log = this.log; + return newDebug; + } + function enable(namespaces) { + createDebug2.save(namespaces); + createDebug2.namespaces = namespaces; + createDebug2.names = []; + createDebug2.skips = []; + let i; + const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/); + const len = split.length; + for (i = 0; i < len; i++) { + if (!split[i]) { + continue; + } + namespaces = split[i].replace(/\*/g, ".*?"); + if (namespaces[0] === "-") { + createDebug2.skips.push(new RegExp("^" + namespaces.slice(1) + "$")); + } else { + createDebug2.names.push(new RegExp("^" + namespaces + "$")); + } + } + } + function disable2() { + const namespaces = [ + ...createDebug2.names.map(toNamespace), + ...createDebug2.skips.map(toNamespace).map((namespace) => "-" + namespace) + ].join(","); + createDebug2.enable(""); + return namespaces; + } + function enabled2(name3) { + if (name3[name3.length - 1] === "*") { + return true; + } + let i; + let len; + for (i = 0, len = createDebug2.skips.length; i < len; i++) { + if (createDebug2.skips[i].test(name3)) { + return false; + } + } + for (i = 0, len = createDebug2.names.length; i < len; i++) { + if (createDebug2.names[i].test(name3)) { + return true; + } + } + return false; + } + function toNamespace(regexp) { + return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*"); + } + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + return val; + } + function destroy() { + console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); + } + createDebug2.enable(createDebug2.load()); + return createDebug2; + } + module.exports = setup; + } +}); + +// node_modules/debug/src/browser.js +var require_browser = __commonJS({ + "node_modules/debug/src/browser.js"(exports, module) { + exports.formatArgs = formatArgs; + exports.save = save; + exports.load = load; + exports.useColors = useColors; + exports.storage = localstorage(); + exports.destroy = /* @__PURE__ */ (() => { + let warned = false; + return () => { + if (!warned) { + warned = true; + console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); + } + }; + })(); + exports.colors = [ + "#0000CC", + "#0000FF", + "#0033CC", + "#0033FF", + "#0066CC", + "#0066FF", + "#0099CC", + "#0099FF", + "#00CC00", + "#00CC33", + "#00CC66", + "#00CC99", + "#00CCCC", + "#00CCFF", + "#3300CC", + "#3300FF", + "#3333CC", + "#3333FF", + "#3366CC", + "#3366FF", + "#3399CC", + "#3399FF", + "#33CC00", + "#33CC33", + "#33CC66", + "#33CC99", + "#33CCCC", + "#33CCFF", + "#6600CC", + "#6600FF", + "#6633CC", + "#6633FF", + "#66CC00", + "#66CC33", + "#9900CC", + "#9900FF", + "#9933CC", + "#9933FF", + "#99CC00", + "#99CC33", + "#CC0000", + "#CC0033", + "#CC0066", + "#CC0099", + "#CC00CC", + "#CC00FF", + "#CC3300", + "#CC3333", + "#CC3366", + "#CC3399", + "#CC33CC", + "#CC33FF", + "#CC6600", + "#CC6633", + "#CC9900", + "#CC9933", + "#CCCC00", + "#CCCC33", + "#FF0000", + "#FF0033", + "#FF0066", + "#FF0099", + "#FF00CC", + "#FF00FF", + "#FF3300", + "#FF3333", + "#FF3366", + "#FF3399", + "#FF33CC", + "#FF33FF", + "#FF6600", + "#FF6633", + "#FF9900", + "#FF9933", + "#FFCC00", + "#FFCC33" + ]; + function useColors() { + if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) { + return true; + } + if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } + return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 + typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker + typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); + } + function formatArgs(args) { + args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff); + if (!this.useColors) { + return; + } + const c3 = "color: " + this.color; + args.splice(1, 0, c3, "color: inherit"); + let index2 = 0; + let lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, (match) => { + if (match === "%%") { + return; + } + index2++; + if (match === "%c") { + lastC = index2; + } + }); + args.splice(lastC, 0, c3); + } + exports.log = console.debug || console.log || (() => { + }); + function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem("debug", namespaces); + } else { + exports.storage.removeItem("debug"); + } + } catch (error) { + } + } + function load() { + let r2; + try { + r2 = exports.storage.getItem("debug"); + } catch (error) { + } + if (!r2 && typeof process !== "undefined" && "env" in process) { + r2 = process.env.DEBUG; + } + return r2; + } + function localstorage() { + try { + return localStorage; + } catch (error) { + } + } + module.exports = require_common()(exports); + var { formatters } = module.exports; + formatters.j = function(v2) { + try { + return JSON.stringify(v2); + } catch (error) { + return "[UnexpectedJSONParseError]: " + error.message; + } + }; + } +}); + +// node_modules/extend/index.js +var require_extend = __commonJS({ + "node_modules/extend/index.js"(exports, module) { + "use strict"; + var hasOwn = Object.prototype.hasOwnProperty; + var toStr = Object.prototype.toString; + var defineProperty = Object.defineProperty; + var gOPD = Object.getOwnPropertyDescriptor; + var isArray = function isArray2(arr) { + if (typeof Array.isArray === "function") { + return Array.isArray(arr); + } + return toStr.call(arr) === "[object Array]"; + }; + var isPlainObject2 = function isPlainObject3(obj) { + if (!obj || toStr.call(obj) !== "[object Object]") { + return false; + } + var hasOwnConstructor = hasOwn.call(obj, "constructor"); + var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, "isPrototypeOf"); + if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { + return false; + } + var key2; + for (key2 in obj) { + } + return typeof key2 === "undefined" || hasOwn.call(obj, key2); + }; + var setProperty = function setProperty2(target, options) { + if (defineProperty && options.name === "__proto__") { + defineProperty(target, options.name, { + enumerable: true, + configurable: true, + value: options.newValue, + writable: true + }); + } else { + target[options.name] = options.newValue; + } + }; + var getProperty = function getProperty2(obj, name3) { + if (name3 === "__proto__") { + if (!hasOwn.call(obj, name3)) { + return void 0; + } else if (gOPD) { + return gOPD(obj, name3).value; + } + } + return obj[name3]; + }; + module.exports = function extend2() { + var options, name3, src, copy, copyIsArray, clone2; + var target = arguments[0]; + var i = 1; + var length = arguments.length; + var deep = false; + if (typeof target === "boolean") { + deep = target; + target = arguments[1] || {}; + i = 2; + } + if (target == null || typeof target !== "object" && typeof target !== "function") { + target = {}; + } + for (; i < length; ++i) { + options = arguments[i]; + if (options != null) { + for (name3 in options) { + src = getProperty(target, name3); + copy = getProperty(options, name3); + if (target !== copy) { + if (deep && copy && (isPlainObject2(copy) || (copyIsArray = isArray(copy)))) { + if (copyIsArray) { + copyIsArray = false; + clone2 = src && isArray(src) ? src : []; + } else { + clone2 = src && isPlainObject2(src) ? src : {}; + } + setProperty(target, { name: name3, newValue: extend2(deep, clone2, copy) }); + } else if (typeof copy !== "undefined") { + setProperty(target, { name: name3, newValue: copy }); + } + } + } + } + } + return target; + }; + } +}); + +// node_modules/@uiw/react-md-editor/esm/Editor.js +var import_react34 = __toESM(require_react()); + +// node_modules/@uiw/react-markdown-preview/esm/index.js +var import_react3 = __toESM(require_react()); + +// node_modules/unist-util-is/lib/index.js +var convert = ( + // Note: overloads in JSDoc can’t yet use different `@template`s. + /** + * @type {( + * ((test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & {type: Condition}) & + * ((test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Condition) & + * ((test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Predicate) & + * ((test?: null | undefined) => (node?: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node) & + * ((test?: Test) => Check) + * )} + */ + /** + * @param {Test} [test] + * @returns {Check} + */ + function(test2) { + if (test2 === null || test2 === void 0) { + return ok; + } + if (typeof test2 === "function") { + return castFactory(test2); + } + if (typeof test2 === "object") { + return Array.isArray(test2) ? anyFactory(test2) : propsFactory(test2); + } + if (typeof test2 === "string") { + return typeFactory(test2); + } + throw new Error("Expected function, string, or object as test"); + } +); +function anyFactory(tests) { + const checks2 = []; + let index2 = -1; + while (++index2 < tests.length) { + checks2[index2] = convert(tests[index2]); + } + return castFactory(any); + function any(...parameters) { + let index3 = -1; + while (++index3 < checks2.length) { + if (checks2[index3].apply(this, parameters)) return true; + } + return false; + } +} +function propsFactory(check) { + const checkAsRecord = ( + /** @type {Record} */ + check + ); + return castFactory(all8); + function all8(node2) { + const nodeAsRecord = ( + /** @type {Record} */ + /** @type {unknown} */ + node2 + ); + let key2; + for (key2 in check) { + if (nodeAsRecord[key2] !== checkAsRecord[key2]) return false; + } + return true; + } +} +function typeFactory(check) { + return castFactory(type); + function type(node2) { + return node2 && node2.type === check; + } +} +function castFactory(testFunction) { + return check; + function check(value, index2, parent2) { + return Boolean( + looksLikeANode(value) && testFunction.call( + this, + value, + typeof index2 === "number" ? index2 : void 0, + parent2 || void 0 + ) + ); + } +} +function ok() { + return true; +} +function looksLikeANode(value) { + return value !== null && typeof value === "object" && "type" in value; +} + +// node_modules/unist-util-visit-parents/lib/color.js +function color(d2) { + return d2; +} + +// node_modules/unist-util-visit-parents/lib/index.js +var empty = []; +var CONTINUE = true; +var EXIT = false; +var SKIP = "skip"; +function visitParents(tree, test2, visitor, reverse) { + let check; + if (typeof test2 === "function" && typeof visitor !== "function") { + reverse = visitor; + visitor = test2; + } else { + check = test2; + } + const is3 = convert(check); + const step = reverse ? -1 : 1; + factory2(tree, void 0, [])(); + function factory2(node2, index2, parents) { + const value = ( + /** @type {Record} */ + node2 && typeof node2 === "object" ? node2 : {} + ); + if (typeof value.type === "string") { + const name3 = ( + // `hast` + typeof value.tagName === "string" ? value.tagName : ( + // `xast` + typeof value.name === "string" ? value.name : void 0 + ) + ); + Object.defineProperty(visit2, "name", { + value: "node (" + color(node2.type + (name3 ? "<" + name3 + ">" : "")) + ")" + }); + } + return visit2; + function visit2() { + let result = empty; + let subresult; + let offset; + let grandparents; + if (!test2 || is3(node2, index2, parents[parents.length - 1] || void 0)) { + result = toResult(visitor(node2, parents)); + if (result[0] === EXIT) { + return result; + } + } + if ("children" in node2 && node2.children) { + const nodeAsParent = ( + /** @type {UnistParent} */ + node2 + ); + if (nodeAsParent.children && result[0] !== SKIP) { + offset = (reverse ? nodeAsParent.children.length : -1) + step; + grandparents = parents.concat(nodeAsParent); + while (offset > -1 && offset < nodeAsParent.children.length) { + const child = nodeAsParent.children[offset]; + subresult = factory2(child, offset, grandparents)(); + if (subresult[0] === EXIT) { + return subresult; + } + offset = typeof subresult[1] === "number" ? subresult[1] : offset + step; + } + } + } + return result; + } + } +} +function toResult(value) { + if (Array.isArray(value)) { + return value; + } + if (typeof value === "number") { + return [CONTINUE, value]; + } + return value === null || value === void 0 ? empty : [value]; +} + +// node_modules/unist-util-visit/lib/index.js +function visit(tree, testOrVisitor, visitorOrReverse, maybeReverse) { + let reverse; + let test2; + let visitor; + if (typeof testOrVisitor === "function" && typeof visitorOrReverse !== "function") { + test2 = void 0; + visitor = testOrVisitor; + reverse = visitorOrReverse; + } else { + test2 = testOrVisitor; + visitor = visitorOrReverse; + reverse = maybeReverse; + } + visitParents(tree, test2, overload, reverse); + function overload(node2, parents) { + const parent2 = parents[parents.length - 1]; + const index2 = parent2 ? parent2.children.indexOf(node2) : void 0; + return visitor(node2, index2, parent2); + } +} + +// node_modules/hast-util-to-string/lib/index.js +function toString(node2) { + if ("children" in node2) { + return all(node2); + } + return "value" in node2 ? node2.value : ""; +} +function one(node2) { + if (node2.type === "text") { + return node2.value; + } + return "children" in node2 ? all(node2) : ""; +} +function all(node2) { + let index2 = -1; + const result = []; + while (++index2 < node2.children.length) { + result[index2] = one(node2.children[index2]); + } + return result.join(""); +} + +// node_modules/unist-util-filter/lib/index.js +var own = {}.hasOwnProperty; +function filter(tree, options, test2) { + const is3 = convert(test2 || options); + const cascadeRaw = options && typeof options === "object" && "cascade" in options ? ( + /** @type {boolean | null | undefined} */ + options.cascade + ) : void 0; + const cascade = cascadeRaw === void 0 || cascadeRaw === null ? true : cascadeRaw; + return preorder(tree); + function preorder(node2, index2, parentNode) { + const children = []; + if (!is3(node2, index2, parentNode)) return void 0; + if (parent(node2)) { + let childIndex = -1; + while (++childIndex < node2.children.length) { + const result = preorder(node2.children[childIndex], childIndex, node2); + if (result) { + children.push(result); + } + } + if (cascade && node2.children.length > 0 && children.length === 0) { + return void 0; + } + } + const next = {}; + let key2; + for (key2 in node2) { + if (own.call(node2, key2)) { + next[key2] = key2 === "children" ? children : node2[key2]; + } + } + return next; + } +} +function parent(node2) { + return "children" in node2 && node2.children !== void 0; +} + +// node_modules/rehype-prism-plus/dist/index.es.js +var import_parse_numeric_range = __toESM(require_parse_numeric_range()); + +// node_modules/refractor/lang/clike.js +clike.displayName = "clike"; +clike.aliases = []; +function clike(Prism2) { + Prism2.languages.clike = { + comment: [ + { + pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/, + lookbehind: true, + greedy: true + }, + { + pattern: /(^|[^\\:])\/\/.*/, + lookbehind: true, + greedy: true + } + ], + string: { + pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + greedy: true + }, + "class-name": { + pattern: /(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i, + lookbehind: true, + inside: { + punctuation: /[.\\]/ + } + }, + keyword: /\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/, + boolean: /\b(?:false|true)\b/, + function: /\b\w+(?=\()/, + number: /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i, + operator: /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/, + punctuation: /[{}[\];(),.:]/ + }; +} + +// node_modules/refractor/lang/c.js +c.displayName = "c"; +c.aliases = []; +function c(Prism2) { + Prism2.register(clike); + Prism2.languages.c = Prism2.languages.extend("clike", { + comment: { + pattern: /\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/, + greedy: true + }, + string: { + // https://en.cppreference.com/w/c/language/string_literal + pattern: /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/, + greedy: true + }, + "class-name": { + pattern: /(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/, + lookbehind: true + }, + keyword: /\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/, + function: /\b[a-z_]\w*(?=\s*\()/i, + number: /(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i, + operator: />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/ + }); + Prism2.languages.insertBefore("c", "string", { + char: { + // https://en.cppreference.com/w/c/language/character_constant + pattern: /'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/, + greedy: true + } + }); + Prism2.languages.insertBefore("c", "string", { + macro: { + // allow for multiline macro definitions + // spaces after the # character compile fine with gcc + pattern: /(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im, + lookbehind: true, + greedy: true, + alias: "property", + inside: { + string: [ + { + // highlight the path of the include statement as a string + pattern: /^(#\s*include\s*)<[^>]+>/, + lookbehind: true + }, + Prism2.languages.c["string"] + ], + char: Prism2.languages.c["char"], + comment: Prism2.languages.c["comment"], + "macro-name": [ + { + pattern: /(^#\s*define\s+)\w+\b(?!\()/i, + lookbehind: true + }, + { + pattern: /(^#\s*define\s+)\w+\b(?=\()/i, + lookbehind: true, + alias: "function" + } + ], + // highlight macro directives as keywords + directive: { + pattern: /^(#\s*)[a-z]+/, + lookbehind: true, + alias: "keyword" + }, + "directive-hash": /^#/, + punctuation: /##|\\(?=[\r\n])/, + expression: { + pattern: /\S[\s\S]*/, + inside: Prism2.languages.c + } + } + } + }); + Prism2.languages.insertBefore("c", "function", { + // highlight predefined macros as constants + constant: /\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/ + }); + delete Prism2.languages.c["boolean"]; +} + +// node_modules/refractor/lang/cpp.js +cpp.displayName = "cpp"; +cpp.aliases = []; +function cpp(Prism2) { + Prism2.register(c); + (function(Prism3) { + var keyword = /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/; + var modName = /\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace( + //g, + function() { + return keyword.source; + } + ); + Prism3.languages.cpp = Prism3.languages.extend("c", { + "class-name": [ + { + pattern: RegExp( + /(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace( + //g, + function() { + return keyword.source; + } + ) + ), + lookbehind: true + }, + // This is intended to capture the class name of method implementations like: + // void foo::bar() const {} + // However! The `foo` in the above example could also be a namespace, so we only capture the class name if + // it starts with an uppercase letter. This approximation should give decent results. + /\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/, + // This will capture the class name before destructors like: + // Foo::~Foo() {} + /\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i, + // This also intends to capture the class name of method implementations but here the class has template + // parameters, so it can't be a namespace (until C++ adds generic namespaces). + /\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/ + ], + keyword, + number: { + pattern: /(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i, + greedy: true + }, + operator: />>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/, + boolean: /\b(?:false|true)\b/ + }); + Prism3.languages.insertBefore("cpp", "string", { + module: { + // https://en.cppreference.com/w/cpp/language/modules + pattern: RegExp( + /(\b(?:import|module)\s+)/.source + "(?:" + // header-name + /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source + "|" + // module name or partition or both + /(?:\s*:\s*)?|:\s*/.source.replace( + //g, + function() { + return modName; + } + ) + ")" + ), + lookbehind: true, + greedy: true, + inside: { + string: /^[<"][\s\S]+/, + operator: /:/, + punctuation: /\./ + } + }, + "raw-string": { + pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/, + alias: "string", + greedy: true + } + }); + Prism3.languages.insertBefore("cpp", "keyword", { + "generic-function": { + pattern: /\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i, + inside: { + function: /^\w+/, + generic: { + pattern: /<[\s\S]+/, + alias: "class-name", + inside: Prism3.languages.cpp + } + } + } + }); + Prism3.languages.insertBefore("cpp", "operator", { + "double-colon": { + pattern: /::/, + alias: "punctuation" + } + }); + Prism3.languages.insertBefore("cpp", "class-name", { + // the base clause is an optional list of parent classes + // https://en.cppreference.com/w/cpp/language/class + "base-clause": { + pattern: /(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/, + lookbehind: true, + greedy: true, + inside: Prism3.languages.extend("cpp", {}) + } + }); + Prism3.languages.insertBefore( + "inside", + "double-colon", + { + // All untokenized words that are not namespaces should be class names + "class-name": /\b[a-z_]\w*\b(?!\s*::)/i + }, + Prism3.languages.cpp["base-clause"] + ); + })(Prism2); +} + +// node_modules/refractor/lang/arduino.js +arduino.displayName = "arduino"; +arduino.aliases = ["ino"]; +function arduino(Prism2) { + Prism2.register(cpp); + Prism2.languages.arduino = Prism2.languages.extend("cpp", { + keyword: /\b(?:String|array|bool|boolean|break|byte|case|catch|continue|default|do|double|else|finally|for|function|goto|if|in|instanceof|int|integer|long|loop|new|null|return|setup|string|switch|throw|try|void|while|word)\b/, + constant: /\b(?:ANALOG_MESSAGE|DEFAULT|DIGITAL_MESSAGE|EXTERNAL|FIRMATA_STRING|HIGH|INPUT|INPUT_PULLUP|INTERNAL|INTERNAL1V1|INTERNAL2V56|LED_BUILTIN|LOW|OUTPUT|REPORT_ANALOG|REPORT_DIGITAL|SET_PIN_MODE|SYSEX_START|SYSTEM_RESET)\b/, + builtin: /\b(?:Audio|BSSID|Bridge|Client|Console|EEPROM|Esplora|EsploraTFT|Ethernet|EthernetClient|EthernetServer|EthernetUDP|File|FileIO|FileSystem|Firmata|GPRS|GSM|GSMBand|GSMClient|GSMModem|GSMPIN|GSMScanner|GSMServer|GSMVoiceCall|GSM_SMS|HttpClient|IPAddress|IRread|Keyboard|KeyboardController|LiquidCrystal|LiquidCrystal_I2C|Mailbox|Mouse|MouseController|PImage|Process|RSSI|RobotControl|RobotMotor|SD|SPI|SSID|Scheduler|Serial|Server|Servo|SoftwareSerial|Stepper|Stream|TFT|Task|USBHost|WiFi|WiFiClient|WiFiServer|WiFiUDP|Wire|YunClient|YunServer|abs|addParameter|analogRead|analogReadResolution|analogReference|analogWrite|analogWriteResolution|answerCall|attach|attachGPRS|attachInterrupt|attached|autoscroll|available|background|beep|begin|beginPacket|beginSD|beginSMS|beginSpeaker|beginTFT|beginTransmission|beginWrite|bit|bitClear|bitRead|bitSet|bitWrite|blink|blinkVersion|buffer|changePIN|checkPIN|checkPUK|checkReg|circle|cityNameRead|cityNameWrite|clear|clearScreen|click|close|compassRead|config|connect|connected|constrain|cos|countryNameRead|countryNameWrite|createChar|cursor|debugPrint|delay|delayMicroseconds|detach|detachInterrupt|digitalRead|digitalWrite|disconnect|display|displayLogos|drawBMP|drawCompass|encryptionType|end|endPacket|endSMS|endTransmission|endWrite|exists|exitValue|fill|find|findUntil|flush|gatewayIP|get|getAsynchronously|getBand|getButton|getCurrentCarrier|getIMEI|getKey|getModifiers|getOemKey|getPINUsed|getResult|getSignalStrength|getSocket|getVoiceCallStatus|getXChange|getYChange|hangCall|height|highByte|home|image|interrupts|isActionDone|isDirectory|isListening|isPIN|isPressed|isValid|keyPressed|keyReleased|keyboardRead|knobRead|leftToRight|line|lineFollowConfig|listen|listenOnLocalhost|loadImage|localIP|lowByte|macAddress|maintain|map|max|messageAvailable|micros|millis|min|mkdir|motorsStop|motorsWrite|mouseDragged|mouseMoved|mousePressed|mouseReleased|move|noAutoscroll|noBlink|noBuffer|noCursor|noDisplay|noFill|noInterrupts|noListenOnLocalhost|noStroke|noTone|onReceive|onRequest|open|openNextFile|overflow|parseCommand|parseFloat|parseInt|parsePacket|pauseMode|peek|pinMode|playFile|playMelody|point|pointTo|position|pow|prepare|press|print|printFirmwareVersion|printVersion|println|process|processInput|pulseIn|put|random|randomSeed|read|readAccelerometer|readBlue|readButton|readBytes|readBytesUntil|readGreen|readJoystickButton|readJoystickSwitch|readJoystickX|readJoystickY|readLightSensor|readMessage|readMicrophone|readNetworks|readRed|readSlider|readString|readStringUntil|readTemperature|ready|rect|release|releaseAll|remoteIP|remoteNumber|remotePort|remove|requestFrom|retrieveCallingNumber|rewindDirectory|rightToLeft|rmdir|robotNameRead|robotNameWrite|run|runAsynchronously|runShellCommand|runShellCommandAsynchronously|running|scanNetworks|scrollDisplayLeft|scrollDisplayRight|seek|sendAnalog|sendDigitalPortPair|sendDigitalPorts|sendString|sendSysex|serialEvent|setBand|setBitOrder|setClockDivider|setCursor|setDNS|setDataMode|setFirmwareVersion|setMode|setPINUsed|setSpeed|setTextSize|setTimeout|shiftIn|shiftOut|shutdown|sin|size|sqrt|startLoop|step|stop|stroke|subnetMask|switchPIN|tan|tempoWrite|text|tone|transfer|tuneWrite|turn|updateIR|userNameRead|userNameWrite|voiceCall|waitContinue|width|write|writeBlue|writeGreen|writeJSON|writeMessage|writeMicroseconds|writeRGB|writeRed|yield)\b/ + }); + Prism2.languages.ino = Prism2.languages.arduino; +} + +// node_modules/refractor/lang/bash.js +bash.displayName = "bash"; +bash.aliases = ["sh", "shell"]; +function bash(Prism2) { + ; + (function(Prism3) { + var envVars = "\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b"; + var commandAfterHeredoc = { + pattern: /(^(["']?)\w+\2)[ \t]+\S.*/, + lookbehind: true, + alias: "punctuation", + // this looks reasonably well in all themes + inside: null + // see below + }; + var insideString = { + bash: commandAfterHeredoc, + environment: { + pattern: RegExp("\\$" + envVars), + alias: "constant" + }, + variable: [ + // [0]: Arithmetic Environment + { + pattern: /\$?\(\([\s\S]+?\)\)/, + greedy: true, + inside: { + // If there is a $ sign at the beginning highlight $(( and )) as variable + variable: [ + { + pattern: /(^\$\(\([\s\S]+)\)\)/, + lookbehind: true + }, + /^\$\(\(/ + ], + number: /\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/, + // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic + operator: /--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/, + // If there is no $ sign at the beginning highlight (( and )) as punctuation + punctuation: /\(\(?|\)\)?|,|;/ + } + }, + // [1]: Command Substitution + { + pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/, + greedy: true, + inside: { + variable: /^\$\(|^`|\)$|`$/ + } + }, + // [2]: Brace expansion + { + pattern: /\$\{[^}]+\}/, + greedy: true, + inside: { + operator: /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/, + punctuation: /[\[\]]/, + environment: { + pattern: RegExp("(\\{)" + envVars), + lookbehind: true, + alias: "constant" + } + } + }, + /\$(?:\w+|[#?*!@$])/ + ], + // Escape sequences from echo and printf's manuals, and escaped quotes. + entity: /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/ + }; + Prism3.languages.bash = { + shebang: { + pattern: /^#!\s*\/.*/, + alias: "important" + }, + comment: { + pattern: /(^|[^"{\\$])#.*/, + lookbehind: true + }, + "function-name": [ + // a) function foo { + // b) foo() { + // c) function foo() { + // but not “foo {” + { + // a) and c) + pattern: /(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/, + lookbehind: true, + alias: "function" + }, + { + // b) + pattern: /\b[\w-]+(?=\s*\(\s*\)\s*\{)/, + alias: "function" + } + ], + // Highlight variable names as variables in for and select beginnings. + "for-or-select": { + pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/, + alias: "variable", + lookbehind: true + }, + // Highlight variable names as variables in the left-hand part + // of assignments (“=” and “+=”). + "assign-left": { + pattern: /(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/, + inside: { + environment: { + pattern: RegExp("(^|[\\s;|&]|[<>]\\()" + envVars), + lookbehind: true, + alias: "constant" + } + }, + alias: "variable", + lookbehind: true + }, + // Highlight parameter names as variables + parameter: { + pattern: /(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/, + alias: "variable", + lookbehind: true + }, + string: [ + // Support for Here-documents https://en.wikipedia.org/wiki/Here_document + { + pattern: /((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/, + lookbehind: true, + greedy: true, + inside: insideString + }, + // Here-document with quotes around the tag + // → No expansion (so no “inside”). + { + pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/, + lookbehind: true, + greedy: true, + inside: { + bash: commandAfterHeredoc + } + }, + // “Normal” string + { + // https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html + pattern: /(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/, + lookbehind: true, + greedy: true, + inside: insideString + }, + { + // https://www.gnu.org/software/bash/manual/html_node/Single-Quotes.html + pattern: /(^|[^$\\])'[^']*'/, + lookbehind: true, + greedy: true + }, + { + // https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html + pattern: /\$'(?:[^'\\]|\\[\s\S])*'/, + greedy: true, + inside: { + entity: insideString.entity + } + } + ], + environment: { + pattern: RegExp("\\$?" + envVars), + alias: "constant" + }, + variable: insideString.variable, + function: { + pattern: /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/, + lookbehind: true + }, + keyword: { + pattern: /(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/, + lookbehind: true + }, + // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html + builtin: { + pattern: /(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/, + lookbehind: true, + // Alias added to make those easier to distinguish from strings. + alias: "class-name" + }, + boolean: { + pattern: /(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/, + lookbehind: true + }, + "file-descriptor": { + pattern: /\B&\d\b/, + alias: "important" + }, + operator: { + // Lots of redirections here, but not just that. + pattern: /\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/, + inside: { + "file-descriptor": { + pattern: /^\d/, + alias: "important" + } + } + }, + punctuation: /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/, + number: { + pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/, + lookbehind: true + } + }; + commandAfterHeredoc.inside = Prism3.languages.bash; + var toBeCopied = [ + "comment", + "function-name", + "for-or-select", + "assign-left", + "parameter", + "string", + "environment", + "function", + "keyword", + "builtin", + "boolean", + "file-descriptor", + "operator", + "punctuation", + "number" + ]; + var inside = insideString.variable[1].inside; + for (var i = 0; i < toBeCopied.length; i++) { + inside[toBeCopied[i]] = Prism3.languages.bash[toBeCopied[i]]; + } + Prism3.languages.sh = Prism3.languages.bash; + Prism3.languages.shell = Prism3.languages.bash; + })(Prism2); +} + +// node_modules/refractor/lang/csharp.js +csharp.displayName = "csharp"; +csharp.aliases = ["cs", "dotnet"]; +function csharp(Prism2) { + Prism2.register(clike); + (function(Prism3) { + function replace2(pattern, replacements) { + return pattern.replace(/<<(\d+)>>/g, function(m, index2) { + return "(?:" + replacements[+index2] + ")"; + }); + } + function re2(pattern, replacements, flags) { + return RegExp(replace2(pattern, replacements), flags || ""); + } + function nested(pattern, depthLog2) { + for (var i = 0; i < depthLog2; i++) { + pattern = pattern.replace(/<>/g, function() { + return "(?:" + pattern + ")"; + }); + } + return pattern.replace(/<>/g, "[^\\s\\S]"); + } + var keywordKinds = { + // keywords which represent a return or variable type + type: "bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void", + // keywords which are used to declare a type + typeDeclaration: "class enum interface record struct", + // contextual keywords + // ("var" and "dynamic" are missing because they are used like types) + contextual: "add alias and ascending async await by descending from(?=\\s*(?:\\w|$)) get global group into init(?=\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\s*{)", + // all other keywords + other: "abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield" + }; + function keywordsToPattern(words) { + return "\\b(?:" + words.trim().replace(/ /g, "|") + ")\\b"; + } + var typeDeclarationKeywords = keywordsToPattern( + keywordKinds.typeDeclaration + ); + var keywords = RegExp( + keywordsToPattern( + keywordKinds.type + " " + keywordKinds.typeDeclaration + " " + keywordKinds.contextual + " " + keywordKinds.other + ) + ); + var nonTypeKeywords = keywordsToPattern( + keywordKinds.typeDeclaration + " " + keywordKinds.contextual + " " + keywordKinds.other + ); + var nonContextualKeywords = keywordsToPattern( + keywordKinds.type + " " + keywordKinds.typeDeclaration + " " + keywordKinds.other + ); + var generic = nested(/<(?:[^<>;=+\-*/%&|^]|<>)*>/.source, 2); + var nestedRound = nested(/\((?:[^()]|<>)*\)/.source, 2); + var name3 = /@?\b[A-Za-z_]\w*\b/.source; + var genericName = replace2(/<<0>>(?:\s*<<1>>)?/.source, [name3, generic]); + var identifier = replace2(/(?!<<0>>)<<1>>(?:\s*\.\s*<<1>>)*/.source, [ + nonTypeKeywords, + genericName + ]); + var array = /\[\s*(?:,\s*)*\]/.source; + var typeExpressionWithoutTuple = replace2( + /<<0>>(?:\s*(?:\?\s*)?<<1>>)*(?:\s*\?)?/.source, + [identifier, array] + ); + var tupleElement = replace2( + /[^,()<>[\];=+\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source, + [generic, nestedRound, array] + ); + var tuple = replace2(/\(<<0>>+(?:,<<0>>+)+\)/.source, [tupleElement]); + var typeExpression = replace2( + /(?:<<0>>|<<1>>)(?:\s*(?:\?\s*)?<<2>>)*(?:\s*\?)?/.source, + [tuple, identifier, array] + ); + var typeInside = { + keyword: keywords, + punctuation: /[<>()?,.:[\]]/ + }; + var character = /'(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'/.source; + var regularString = /"(?:\\.|[^\\"\r\n])*"/.source; + var verbatimString = /@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source; + Prism3.languages.csharp = Prism3.languages.extend("clike", { + string: [ + { + pattern: re2(/(^|[^$\\])<<0>>/.source, [verbatimString]), + lookbehind: true, + greedy: true + }, + { + pattern: re2(/(^|[^@$\\])<<0>>/.source, [regularString]), + lookbehind: true, + greedy: true + } + ], + "class-name": [ + { + // Using static + // using static System.Math; + pattern: re2(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source, [ + identifier + ]), + lookbehind: true, + inside: typeInside + }, + { + // Using alias (type) + // using Project = PC.MyCompany.Project; + pattern: re2(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source, [ + name3, + typeExpression + ]), + lookbehind: true, + inside: typeInside + }, + { + // Using alias (alias) + // using Project = PC.MyCompany.Project; + pattern: re2(/(\busing\s+)<<0>>(?=\s*=)/.source, [name3]), + lookbehind: true + }, + { + // Type declarations + // class Foo + // interface Foo + pattern: re2(/(\b<<0>>\s+)<<1>>/.source, [ + typeDeclarationKeywords, + genericName + ]), + lookbehind: true, + inside: typeInside + }, + { + // Single catch exception declaration + // catch(Foo) + // (things like catch(Foo e) is covered by variable declaration) + pattern: re2(/(\bcatch\s*\(\s*)<<0>>/.source, [identifier]), + lookbehind: true, + inside: typeInside + }, + { + // Name of the type parameter of generic constraints + // where Foo : class + pattern: re2(/(\bwhere\s+)<<0>>/.source, [name3]), + lookbehind: true + }, + { + // Casts and checks via as and is. + // as Foo, is Bar + // (things like if(a is Foo b) is covered by variable declaration) + pattern: re2(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source, [ + typeExpressionWithoutTuple + ]), + lookbehind: true, + inside: typeInside + }, + { + // Variable, field and parameter declaration + // (Foo bar, Bar baz, Foo[,,] bay, Foo> bax) + pattern: re2( + /\b<<0>>(?=\s+(?!<<1>>|with\s*\{)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source, + [typeExpression, nonContextualKeywords, name3] + ), + inside: typeInside + } + ], + keyword: keywords, + // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#literals + number: /(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i, + operator: />>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/, + punctuation: /\?\.?|::|[{}[\];(),.:]/ + }); + Prism3.languages.insertBefore("csharp", "number", { + range: { + pattern: /\.\./, + alias: "operator" + } + }); + Prism3.languages.insertBefore("csharp", "punctuation", { + "named-parameter": { + pattern: re2(/([(,]\s*)<<0>>(?=\s*:)/.source, [name3]), + lookbehind: true, + alias: "punctuation" + } + }); + Prism3.languages.insertBefore("csharp", "class-name", { + namespace: { + // namespace Foo.Bar {} + // using Foo.Bar; + pattern: re2( + /(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source, + [name3] + ), + lookbehind: true, + inside: { + punctuation: /\./ + } + }, + "type-expression": { + // default(Foo), typeof(Foo), sizeof(int) + pattern: re2( + /(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source, + [nestedRound] + ), + lookbehind: true, + alias: "class-name", + inside: typeInside + }, + "return-type": { + // Foo ForBar(); Foo IFoo.Bar() => 0 + // int this[int index] => 0; T IReadOnlyList.this[int index] => this[index]; + // int Foo => 0; int Foo { get; set } = 0; + pattern: re2( + /<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source, + [typeExpression, identifier] + ), + inside: typeInside, + alias: "class-name" + }, + "constructor-invocation": { + // new List> { } + pattern: re2(/(\bnew\s+)<<0>>(?=\s*[[({])/.source, [typeExpression]), + lookbehind: true, + inside: typeInside, + alias: "class-name" + }, + /*'explicit-implementation': { + // int IFoo.Bar => 0; void IFoo>.Foo(); + pattern: replace(/\b<<0>>(?=\.<<1>>)/, className, methodOrPropertyDeclaration), + inside: classNameInside, + alias: 'class-name' + },*/ + "generic-method": { + // foo() + pattern: re2(/<<0>>\s*<<1>>(?=\s*\()/.source, [name3, generic]), + inside: { + function: re2(/^<<0>>/.source, [name3]), + generic: { + pattern: RegExp(generic), + alias: "class-name", + inside: typeInside + } + } + }, + "type-list": { + // The list of types inherited or of generic constraints + // class Foo : Bar, IList + // where F : Bar, IList + pattern: re2( + /\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/.source, + [ + typeDeclarationKeywords, + genericName, + name3, + typeExpression, + keywords.source, + nestedRound, + /\bnew\s*\(\s*\)/.source + ] + ), + lookbehind: true, + inside: { + "record-arguments": { + pattern: re2(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source, [ + genericName, + nestedRound + ]), + lookbehind: true, + greedy: true, + inside: Prism3.languages.csharp + }, + keyword: keywords, + "class-name": { + pattern: RegExp(typeExpression), + greedy: true, + inside: typeInside + }, + punctuation: /[,()]/ + } + }, + preprocessor: { + pattern: /(^[\t ]*)#.*/m, + lookbehind: true, + alias: "property", + inside: { + // highlight preprocessor directives as keywords + directive: { + pattern: /(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/, + lookbehind: true, + alias: "keyword" + } + } + } + }); + var regularStringOrCharacter = regularString + "|" + character; + var regularStringCharacterOrComment = replace2( + /\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source, + [regularStringOrCharacter] + ); + var roundExpression = nested( + replace2(/[^"'/()]|<<0>>|\(<>*\)/.source, [ + regularStringCharacterOrComment + ]), + 2 + ); + var attrTarget = /\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source; + var attr = replace2(/<<0>>(?:\s*\(<<1>>*\))?/.source, [ + identifier, + roundExpression + ]); + Prism3.languages.insertBefore("csharp", "class-name", { + attribute: { + // Attributes + // [Foo], [Foo(1), Bar(2, Prop = "foo")], [return: Foo(1), Bar(2)], [assembly: Foo(Bar)] + pattern: re2( + /((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source, + [attrTarget, attr] + ), + lookbehind: true, + greedy: true, + inside: { + target: { + pattern: re2(/^<<0>>(?=\s*:)/.source, [attrTarget]), + alias: "keyword" + }, + "attribute-arguments": { + pattern: re2(/\(<<0>>*\)/.source, [roundExpression]), + inside: Prism3.languages.csharp + }, + "class-name": { + pattern: RegExp(identifier), + inside: { + punctuation: /\./ + } + }, + punctuation: /[:,]/ + } + } + }); + var formatString = /:[^}\r\n]+/.source; + var mInterpolationRound = nested( + replace2(/[^"'/()]|<<0>>|\(<>*\)/.source, [ + regularStringCharacterOrComment + ]), + 2 + ); + var mInterpolation = replace2(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [ + mInterpolationRound, + formatString + ]); + var sInterpolationRound = nested( + replace2( + /[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<>*\)/.source, + [regularStringOrCharacter] + ), + 2 + ); + var sInterpolation = replace2(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [ + sInterpolationRound, + formatString + ]); + function createInterpolationInside(interpolation, interpolationRound) { + return { + interpolation: { + pattern: re2(/((?:^|[^{])(?:\{\{)*)<<0>>/.source, [interpolation]), + lookbehind: true, + inside: { + "format-string": { + pattern: re2(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source, [ + interpolationRound, + formatString + ]), + lookbehind: true, + inside: { + punctuation: /^:/ + } + }, + punctuation: /^\{|\}$/, + expression: { + pattern: /[\s\S]+/, + alias: "language-csharp", + inside: Prism3.languages.csharp + } + } + }, + string: /[\s\S]+/ + }; + } + Prism3.languages.insertBefore("csharp", "string", { + "interpolation-string": [ + { + pattern: re2( + /(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source, + [mInterpolation] + ), + lookbehind: true, + greedy: true, + inside: createInterpolationInside(mInterpolation, mInterpolationRound) + }, + { + pattern: re2(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source, [ + sInterpolation + ]), + lookbehind: true, + greedy: true, + inside: createInterpolationInside(sInterpolation, sInterpolationRound) + } + ], + char: { + pattern: RegExp(character), + greedy: true + } + }); + Prism3.languages.dotnet = Prism3.languages.cs = Prism3.languages.csharp; + })(Prism2); +} + +// node_modules/refractor/lang/markup.js +markup.displayName = "markup"; +markup.aliases = ["atom", "html", "mathml", "rss", "ssml", "svg", "xml"]; +function markup(Prism2) { + Prism2.languages.markup = { + comment: { + pattern: //, + greedy: true + }, + prolog: { + pattern: /<\?[\s\S]+?\?>/, + greedy: true + }, + doctype: { + // https://www.w3.org/TR/xml/#NT-doctypedecl + pattern: /"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i, + greedy: true, + inside: { + "internal-subset": { + pattern: /(^[^\[]*\[)[\s\S]+(?=\]>$)/, + lookbehind: true, + greedy: true, + inside: null + // see below + }, + string: { + pattern: /"[^"]*"|'[^']*'/, + greedy: true + }, + punctuation: /^$|[[\]]/, + "doctype-tag": /^DOCTYPE/i, + name: /[^\s<>'"]+/ + } + }, + cdata: { + pattern: //i, + greedy: true + }, + tag: { + pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/, + greedy: true, + inside: { + tag: { + pattern: /^<\/?[^\s>\/]+/, + inside: { + punctuation: /^<\/?/, + namespace: /^[^\s>\/:]+:/ + } + }, + "special-attr": [], + "attr-value": { + pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/, + inside: { + punctuation: [ + { + pattern: /^=/, + alias: "attr-equals" + }, + { + pattern: /^(\s*)["']|["']$/, + lookbehind: true + } + ] + } + }, + punctuation: /\/?>/, + "attr-name": { + pattern: /[^\s>\/]+/, + inside: { + namespace: /^[^\s>\/:]+:/ + } + } + } + }, + entity: [ + { + pattern: /&[\da-z]{1,8};/i, + alias: "named-entity" + }, + /&#x?[\da-f]{1,8};/i + ] + }; + Prism2.languages.markup["tag"].inside["attr-value"].inside["entity"] = Prism2.languages.markup["entity"]; + Prism2.languages.markup["doctype"].inside["internal-subset"].inside = Prism2.languages.markup; + Prism2.hooks.add("wrap", function(env2) { + if (env2.type === "entity") { + env2.attributes["title"] = env2.content.value.replace(/&/, "&"); + } + }); + Object.defineProperty(Prism2.languages.markup.tag, "addInlined", { + /** + * Adds an inlined language to markup. + * + * An example of an inlined language is CSS with `