diff --git a/Cargo.lock b/Cargo.lock index d5621431a..50b68f909 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3081,6 +3081,28 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "kinode_process_lib" +version = "0.6.1" +source = "git+https://github.com/kinode-dao/process_lib?tag=v0.6.1#37a20b0249dc2c86ae6c2c69cfb199fb177f1520" +dependencies = [ + "alloy-json-rpc 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=6f8ebb4)", + "alloy-primitives 0.6.4", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=6f8ebb4)", + "alloy-transport 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=6f8ebb4)", + "anyhow", + "bincode", + "http 1.1.0", + "mime_guess", + "rand 0.8.5", + "rmp-serde", + "serde", + "serde_json", + "thiserror", + "url", + "wit-bindgen", +] + [[package]] name = "kinode_process_lib" version = "0.7.0" @@ -4812,6 +4834,21 @@ dependencies = [ "serde", ] +[[package]] +name = "settings" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64 0.22.0", + "bincode", + "kinode_process_lib 0.6.1", + "rmp-serde", + "serde", + "serde_json", + "url", + "wit-bindgen", +] + [[package]] name = "sha1" version = "0.10.6" diff --git a/Cargo.toml b/Cargo.toml index 6cc9242d4..38d8b3a19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "kinode/packages/chess/chess", "kinode/packages/homepage/homepage", "kinode/packages/kns_indexer/kns_indexer", "kinode/packages/kns_indexer/get_block", "kinode/packages/kns_indexer/state", + "kinode/packages/settings/settings", "kinode/packages/terminal/terminal", "kinode/packages/terminal/alias", "kinode/packages/terminal/cat", "kinode/packages/terminal/echo", "kinode/packages/terminal/hi", "kinode/packages/terminal/m", "kinode/packages/terminal/top", "kinode/packages/terminal/namehash_to_name", "kinode/packages/terminal/net_diagnostics", "kinode/packages/terminal/peer", "kinode/packages/terminal/peers", diff --git a/kinode/packages/app_store/ui/buidl.sh b/kinode/packages/app_store/ui/build.sh similarity index 100% rename from kinode/packages/app_store/ui/buidl.sh rename to kinode/packages/app_store/ui/build.sh diff --git a/kinode/packages/homepage/homepage/src/lib.rs b/kinode/packages/homepage/homepage/src/lib.rs index f2b24821d..729760618 100644 --- a/kinode/packages/homepage/homepage/src/lib.rs +++ b/kinode/packages/homepage/homepage/src/lib.rs @@ -5,10 +5,10 @@ use kinode_process_lib::{ bind_http_path, bind_http_static_path, send_response, serve_ui, HttpServerError, HttpServerRequest, StatusCode, }, - println, Address, Message, ProcessId, + println, Address, Message, }; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::collections::BTreeMap; /// The request format to add or remove an app from the homepage. You must have messaging /// access to `homepage:homepage:sys` in order to perform this. Serialize using serde_json. @@ -40,7 +40,7 @@ wit_bindgen::generate!({ call_init!(init); fn init(our: Address) { - let mut app_data: HashMap = HashMap::new(); + let mut app_data: BTreeMap = BTreeMap::new(); serve_ui(&our, "ui", true, false, vec!["/"]).expect("failed to serve ui"); @@ -86,7 +86,7 @@ fn init(our: Address) { match request { HomepageRequest::Add { label, icon, path } => { app_data.insert( - message.source().process.clone(), + message.source().process.to_string(), HomepageApp { package_name: message.source().clone().package().to_string(), path: format!( @@ -113,7 +113,7 @@ fn init(our: Address) { if path == "/apps" { send_response( StatusCode::OK, - Some(HashMap::from([( + Some(std::collections::HashMap::from([( "Content-Type".to_string(), "application/json".to_string(), )])), diff --git a/kinode/packages/settings/metadata.json b/kinode/packages/settings/metadata.json new file mode 100644 index 000000000..aae6196bb --- /dev/null +++ b/kinode/packages/settings/metadata.json @@ -0,0 +1,16 @@ +{ + "name": "System Settings", + "description": "A program for managing key system settings, including onchain identity.", + "image": "", + "properties": { + "package_name": "settings", + "current_version": "0.1.0", + "publisher": "sys", + "mirrors": [], + "code_hashes": { + "0.1.0": "" + } + }, + "external_url": "https://kinode.org", + "animation_url": "" +} \ No newline at end of file diff --git a/kinode/packages/settings/pkg/manifest.json b/kinode/packages/settings/pkg/manifest.json new file mode 100644 index 000000000..c6c457d35 --- /dev/null +++ b/kinode/packages/settings/pkg/manifest.json @@ -0,0 +1,30 @@ +[ + { + "process_name": "settings", + "process_wasm_path": "/settings.wasm", + "on_exit": "Restart", + "request_networking": true, + "request_capabilities": [ + "eth:distro:sys", + { + "process": "eth:distro:sys", + "params": { + "root": true + } + }, + "homepage:homepage:sys", + "http_server:distro:sys", + "kernel:distro:sys", + "net:distro:sys", + "vfs:distro:sys" + ], + "grant_capabilities": [ + "eth:distro:sys", + "http_server:distro:sys", + "kernel:distro:sys", + "net:distro:sys", + "vfs:distro:sys" + ], + "public": false + } +] \ No newline at end of file diff --git a/kinode/packages/settings/pkg/ui/index.html b/kinode/packages/settings/pkg/ui/index.html new file mode 100644 index 000000000..e9e86cdaa --- /dev/null +++ b/kinode/packages/settings/pkg/ui/index.html @@ -0,0 +1,76 @@ + + + + + + + + Kinode Settings + + + + +

system diagnostics & settings

+
+
+

node info

+

+

+

+

+ + + +
+ +
+

networking diagnostics

+

+
+
+

fetch PKI data

+
+ + +
+

+

ping a node

+
+ + + + +
+

+
+ +
+

ETH RPC providers

+
    +
    + +
    +

    ETH RPC settings

    +

    +
    +

    nodes allowed to connect:

    +
      +
      +
      +

      nodes banned from connecting:

      +
        +
        +
        + +
        +

        running processes:

        +

        (TODO)

        +
          +
          + + + +
          + + + \ No newline at end of file diff --git a/kinode/packages/settings/pkg/ui/script.js b/kinode/packages/settings/pkg/ui/script.js new file mode 100644 index 000000000..c61d9f6d0 --- /dev/null +++ b/kinode/packages/settings/pkg/ui/script.js @@ -0,0 +1,167 @@ +const APP_PATH = '/settings:settings:sys/ask'; + +// Fetch initial data and populate the UI +function init() { + fetch('/our') + .then(response => response.text()) + .then(data => { + const our = data + '@settings:settings:sys'; + fetch(APP_PATH) + .then(response => response.json()) + .then(data => { + console.log(data); + populate(data); + }); + }); +} + +function api_call(body) { + fetch(APP_PATH, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); +} + +function shutdown() { + api_call("Shutdown"); +} + +function populate(data) { + populate_node_info(data.identity); + populate_net_diagnostics(data.diagnostics); + populate_eth_rpc_providers(data.eth_rpc_providers); + populate_eth_rpc_settings(data.eth_rpc_access_settings); + // populate_kernel() +} + +function populate_node_info(identity) { + document.getElementById('node-name').innerText = identity.name; + document.getElementById('net-key').innerText = identity.networking_key; + if (identity.ws_routing) { + document.getElementById('ip-ports').innerText = identity.ws_routing; + } else { + document.getElementById('ip-ports').style.display = 'none'; + } + if (identity.routers) { + document.getElementById('routers').innerText = identity.routers; + } else { + document.getElementById('routers').style.display = 'none'; + } +} + +function populate_net_diagnostics(diagnostics) { + document.getElementById('diagnostics').innerText = diagnostics; +} + +function populate_eth_rpc_providers(providers) { + const ul = document.getElementById('providers'); + ul.innerHTML = ''; + providers.forEach(provider => { + const li = document.createElement('li'); + li.innerHTML = `
        • ${JSON.stringify(provider)}
        • `; + ul.appendChild(li); + }); +} + +function populate_eth_rpc_settings(settings) { + if (settings.public) { + document.getElementById('public').innerText = 'public'; + document.getElementById('allowed-nodes').style.display = 'none'; + } else { + document.getElementById('public').innerText = 'private'; + const ul = document.getElementById('allowed-nodes'); + ul.innerHTML = ''; + if (settings.allow.length === 0) { + const li = document.createElement('li'); + li.innerHTML = `
        • (none)
        • `; + ul.appendChild(li); + } else { + settings.allow.forEach(allowed_node => { + const li = document.createElement('li'); + li.innerHTML = `
        • ${allowed_node}
        • `; + ul.appendChild(li); + }); + } + } + const ul = document.getElementById('denied-nodes'); + ul.innerHTML = ''; + if (settings.deny.length === 0) { + const li = document.createElement('li'); + li.innerHTML = `
        • (none)
        • `; + ul.appendChild(li); + } else { + settings.deny.forEach(denied_node => { + const li = document.createElement('li'); + li.innerHTML = `
        • ${denied_node}
        • `; + ul.appendChild(li); + }); + } +} + +// Call init to start the application +init(); + +// Setup event listeners +document.getElementById('shutdown').addEventListener('click', shutdown); + +document.getElementById('get-peer-pki').addEventListener('submit', (e) => { + e.preventDefault(); + const data = new FormData(e.target); + const body = { + "PeerId": data.get('peer'), + }; + fetch(APP_PATH, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }).then(response => response.json()) + .then(data => { + if (data === null) { + document.getElementById('peer-pki-response').innerText = "no pki data for peer"; + } else { + document.getElementById('peer-pki-response').innerText = JSON.stringify(data); + } + }); +}) + +document.getElementById('ping-peer').addEventListener('submit', (e) => { + e.preventDefault(); + const data = new FormData(e.target); + const body = { + "Hi": { + node: data.get('peer'), + content: data.get('content'), + timeout: Number(data.get('timeout')), + } + }; + fetch(APP_PATH, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }).then(response => response.json()) + .then(data => { + if (data === null) { + document.getElementById('peer-ping-response').innerText = "ping successful!"; + } else if (data === "HiTimeout") { + document.getElementById('peer-ping-response').innerText = "node timed out"; + } else if (data === "HiOffline") { + document.getElementById('peer-ping-response').innerText = "node is offline"; + } + }); +}) + +// Setup WebSocket connection +const ws = new WebSocket("ws://" + location.host + "/settings:settings:sys/"); +ws.onmessage = event => { + const data = JSON.parse(event.data); + console.log(data); + populate(data); +}; + diff --git a/kinode/packages/settings/pkg/ui/style.css b/kinode/packages/settings/pkg/ui/style.css new file mode 100644 index 000000000..7121eea35 --- /dev/null +++ b/kinode/packages/settings/pkg/ui/style.css @@ -0,0 +1,181 @@ +/* CSS Reset from https://www.joshwcomeau.com/css/custom-css-reset/ */ + +/* + 1. Use a more-intuitive box-sizing model. +*/ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* + 2. Remove default margin + */ +* { + margin: 0; +} + +/* + Typographic tweaks! + 3. Add accessible line-height + 4. Improve text rendering + */ +body { + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +/* + 5. Improve media defaults + */ +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; +} + +/* + 6. Remove built-in form typography styles + */ +input, +button, +textarea, +select { + font: inherit; +} + +/* + 7. Avoid text overflows + */ +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +/* + 8. Create a root stacking context + */ +#root, +#__next { + isolation: isolate; +} + +/* Actual styles */ + +body { + font-family: 'Courier New', Courier, monospace; + background-color: #1a1a1a; + color: #f0f0f0; +} + +h1 { + padding: 20px; +} + +main { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + padding: 20px; + max-width: 1200px; + min-width: 300px; +} + +article { + background-color: #333; + border: 1px solid #444; + padding: 20px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + max-height: 600px; + overflow-y: auto; +} + +/* Custom scrollbar styles */ +article::-webkit-scrollbar { + width: 8px; +} + +article::-webkit-scrollbar-track { + background: #2c2c2c; +} + +article::-webkit-scrollbar-thumb { + background-color: #444; + border-radius: 4px; +} + +button { + background-color: #4CAF50; + color: white; + border: none; + padding: 10px 20px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + transition-duration: 0.4s; + cursor: pointer; + border-radius: 4px; +} + +button:hover { + background-color: white; + color: #4CAF50; +} + +input[type="text"], +input[type="number"], +select, +textarea { + width: 100%; + padding: 12px 20px; + margin: 8px 0; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +input[type="submit"] { + background-color: #f44336; + color: white; + border: none; + padding: 10px 20px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + transition-duration: 0.4s; + cursor: pointer; + border-radius: 4px; +} + +input[type="submit"]:hover { + background-color: white; + color: #f44336; +} + +ul { + list-style-type: none; + padding: 0; +} + +li { + padding: 8px; + margin-bottom: 6px; + background-color: #2c2c2c; + border-radius: 4px; + word-wrap: break-word; +} \ No newline at end of file diff --git a/kinode/packages/settings/settings/Cargo.lock b/kinode/packages/settings/settings/Cargo.lock new file mode 100644 index 000000000..b5f3d9b29 --- /dev/null +++ b/kinode/packages/settings/settings/Cargo.lock @@ -0,0 +1,835 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chess" +version = "0.2.0" +dependencies = [ + "anyhow", + "base64", + "bincode", + "kinode_process_lib", + "pleco", + "serde", + "serde_json", + "url", + "wit-bindgen", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "kinode_process_lib" +<<<<<<< HEAD:modules/chess/chess/Cargo.lock +version = "0.5.7" +source = "git+https://github.com/kinode-dao/process_lib?tag=v0.5.9-alpha#c1ac7227951fbd8cabf6568704f0ce11e8558c8a" +======= +version = "0.5.6" +source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7" +>>>>>>> develop:kinode/packages/chess/chess/Cargo.lock +dependencies = [ + "anyhow", + "bincode", + "http", + "mime_guess", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror", + "url", + "wit-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mucow" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55d0c9dc43dedfd2414deb74ade67687749ef88b1d3482024d4c81d901a7a83" + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pleco" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a8c8ab569c544644c468a63f4fe4b33c0706b1472bebb517fabb75ec0f688e" +dependencies = [ + "bitflags 1.3.2", + "lazy_static", + "mucow", + "num_cpus", + "rand 0.6.5", + "rayon", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rayon" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "spdx" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b" +dependencies = [ + "smallvec", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-encoder" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e09bca7d6388637d27fb5edbeab11f56bfabcef8743c55ae34370e1e5030a071" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.10.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c853d3809fc9fccf3bc0ad63f4f51d8eefad0bacf88f957aa991c1d9b88b016e" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder 0.41.0", + "wasmparser 0.121.0", +] + +[[package]] +name = "wasmparser" +version = "0.118.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" +dependencies = [ + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.121.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8" +dependencies = [ + "bitflags 2.4.2", + "indexmap", + "semver", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "wit-bindgen" +version = "0.16.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +dependencies = [ + "bitflags 2.4.2", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.16.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +dependencies = [ + "anyhow", + "wit-component", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.16.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +dependencies = [ + "anyhow", + "heck", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.16.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", + "wit-component", +] + +[[package]] +name = "wit-component" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2" +dependencies = [ + "anyhow", + "bitflags 2.4.2", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.38.1", + "wasm-metadata", + "wasmparser 0.118.1", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] diff --git a/kinode/packages/settings/settings/Cargo.toml b/kinode/packages/settings/settings/Cargo.toml new file mode 100644 index 000000000..8efff1347 --- /dev/null +++ b/kinode/packages/settings/settings/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "settings" +version = "0.1.0" +edition = "2021" + +[features] +simulation-mode = [] + +[dependencies] +anyhow = "1.0" +base64 = "0.22.0" +bincode = "1.3.3" +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.6.1" } +rmp-serde = "1.2.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +url = "*" +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/kinode/packages/settings/settings/src/icon b/kinode/packages/settings/settings/src/icon new file mode 100644 index 000000000..682232436 --- /dev/null +++ b/kinode/packages/settings/settings/src/icon @@ -0,0 +1 @@ +data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIiA/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgd2lkdGg9IjEwODAiIGhlaWdodD0iMTA4MCIgdmlld0JveD0iMCAwIDEwODAgMTA4MCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxkZXNjPkNyZWF0ZWQgd2l0aCBGYWJyaWMuanMgNS4yLjQ8L2Rlc2M+CjxkZWZzPgo8L2RlZnM+CjxnIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIDEgNTQwIDU0MCkiIGlkPSI0MDhjZjk5Ni0xN2M1LTQxOTctYmVhMy1jNTQwZGJhYTlhZGUiICA+CjxyZWN0IHN0eWxlPSJzdHJva2U6IG5vbmU7IHN0cm9rZS13aWR0aDogMTsgc3Ryb2tlLWRhc2hhcnJheTogbm9uZTsgc3Ryb2tlLWxpbmVjYXA6IGJ1dHQ7IHN0cm9rZS1kYXNob2Zmc2V0OiAwOyBzdHJva2UtbGluZWpvaW46IG1pdGVyOyBzdHJva2UtbWl0ZXJsaW1pdDogNDsgZmlsbDogcmdiKDI1NSwyNTUsMjU1KTsgZmlsbC1ydWxlOiBub256ZXJvOyBvcGFjaXR5OiAxOyB2aXNpYmlsaXR5OiBoaWRkZW47IiB2ZWN0b3ItZWZmZWN0PSJub24tc2NhbGluZy1zdHJva2UiICB4PSItNTQwIiB5PSItNTQwIiByeD0iMCIgcnk9IjAiIHdpZHRoPSIxMDgwIiBoZWlnaHQ9IjEwODAiIC8+CjwvZz4KPGcgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgMSA1NDAgNTQwKSIgaWQ9ImM1MzhkOTdkLWFhNjAtNGRmNi05NzM1LTE4N2FlOGU3ODQwYyIgID4KPC9nPgo8ZyB0cmFuc2Zvcm09Im1hdHJpeChOYU4gTmFOIE5hTiBOYU4gMCAwKSIgID4KPGcgc3R5bGU9IiIgICA+CjwvZz4KPC9nPgo8ZyB0cmFuc2Zvcm09Im1hdHJpeCgyLjExIDAgMCAyLjExIDU0MCA1NDApIiAgPgo8cGF0aCBzdHlsZT0ic3Ryb2tlOiByZ2IoMCwwLDApOyBzdHJva2Utd2lkdGg6IDA7IHN0cm9rZS1kYXNoYXJyYXk6IG5vbmU7IHN0cm9rZS1saW5lY2FwOiBidXR0OyBzdHJva2UtZGFzaG9mZnNldDogMDsgc3Ryb2tlLWxpbmVqb2luOiBtaXRlcjsgc3Ryb2tlLW1pdGVybGltaXQ6IDQ7IGZpbGw6IHJnYigwLDAsMCk7IGZpbGwtcnVsZTogbm9uemVybzsgb3BhY2l0eTogMTsiICB0cmFuc2Zvcm09IiB0cmFuc2xhdGUoLTI1NiwgLTI1NikiIGQ9Ik0gNDEzLjk2NyAyNzYuOCBDIDQxNS4wMjcgMjcwLjU2NSA0MTUuMDI3IDI2My4yODIwMDAwMDAwMDAwNCA0MTUuMDI3IDI1NiBDIDQxNS4wMjcgMjQ4LjcxNzk5OTk5OTk5OTk2IDQxMy45NjcgMjQyLjQ4MiA0MTMuOTY3IDIzNS4yIEwgNDU4LjYzNCAyMDAuODgyIEMgNDYyLjg5NCAxOTcuNzY0IDQ2My45NTMwMDAwMDAwMDAwMyAxOTIuNTY1IDQ2MC43NjQgMTg3LjM2NCBMIDQxOC4yMTUgMTE1LjYgQyA0MTYuMDg1OTk5OTk5OTk5OTYgMTExLjQzNTk5OTk5OTk5OTk5IDQwOS43MDc5OTk5OTk5OTk5NyAxMDkuMzY1IDQwNS40NDggMTExLjQzNTk5OTk5OTk5OTk5IEwgMzUyLjI2MiAxMzIuMjM3IEMgMzQxLjYyNCAxMjMuOTE5IDMyOC44NjggMTE2LjYzNiAzMTYuMTAyIDExMS40MzU5OTk5OTk5OTk5OSBMIDMwOC42NTQgNTYuMzE4OTk5OTk5OTk5OTk2IEMgMzA3LjU5NCA1Mi4xNjQ5OTk5OTk5OTk5OSAzMDMuMzM1IDQ4LjAwMSAyOTguMDE2IDQ4LjAwMSBMIDIxMi45MTggNDguMDAxIEMgMjA3LjYgNDguMDAxIDIwMy4zNDEgNTIuMTY1IDIwMi4yODEgNTYuMzE4OTk5OTk5OTk5OTk2IEwgMTkzLjc3MyAxMTEuNDM1OTk5OTk5OTk5OTkgQyAxODEuMDA2IDExNi42MzYgMTY5LjMwOSAxMjMuOTE3OTk5OTk5OTk5OTkgMTU3LjYwMiAxMzIuMjM3IEwgMTA0LjQxNiAxMTEuNDM1OTk5OTk5OTk5OTkgQyA5OS4wOTcgMTA5LjM2NSA5My43Nzc5OTk5OTk5OTk5OSAxMTEuNDM1OTk5OTk5OTk5OTkgOTEuNjQ5IDExNS42IEwgNDkuMSAxODcuMzY1IEMgNDYuOTgxIDE5MS41MTggNDguMDM5IDE5Ny43NjQgNTEuMjI5IDIwMC44ODMgTCA5Ni45NyAyMzUuMiBDIDk2Ljk3IDI0Mi40ODIgOTUuOTEgMjQ4LjcxOCA5NS45MSAyNTYgQyA5NS45MSAyNjMuMjgyMDAwMDAwMDAwMDQgOTYuOTcgMjY5LjUxODAwMDAwMDAwMDAzIDk2Ljk3IDI3Ni44IEwgNTIuMzAyIDMxMS4xMTggQyA0OC4wNDIgMzE0LjIzNiA0Ni45ODQgMzE5LjQzNSA1MC4xNzIgMzI0LjYzNTk5OTk5OTk5OTk3IEwgOTIuNzIxIDM5Ni40IEMgOTQuODUxIDQwMC41NjM5OTk5OTk5OTk5NiAxMDEuMjI5IDQwMi42MzUgMTA1LjQ4OCA0MDAuNTYzOTk5OTk5OTk5OTYgTCAxNTguNjc1IDM3OS43NjMgQyAxNjkuMzEyIDM4OC4wODA5OTk5OTk5OTk5NiAxODIuMDY5MDAwMDAwMDAwMDIgMzk1LjM2NCAxOTQuODM1IDQwMC41NjM5OTk5OTk5OTk5NiBMIDIwMy4zNDMwMDAwMDAwMDAwMiA0NTUuNjgxIEMgMjA0LjQxMiA0NjAuODgxIDIwOC42NjEwMDAwMDAwMDAwMyA0NjMuOTk4OTk5OTk5OTk5OTcgMjEzLjk4MDAwMDAwMDAwMDAyIDQ2My45OTg5OTk5OTk5OTk5NyBMIDI5OS4wNzgwMDAwMDAwMDAwMyA0NjMuOTk4OTk5OTk5OTk5OTcgQyAzMDQuMzk3MDAwMDAwMDAwMDUgNDYzLjk5ODk5OTk5OTk5OTk3IDMwOC42NTYgNDU5LjgzNSAzMDkuNzE2IDQ1NS42ODEgTCAzMTguMjM0MDAwMDAwMDAwMDQgNDAwLjU2Mzk5OTk5OTk5OTk2IEMgMzMwLjk5MTAwMDAwMDAwMDA0IDM5NS4zNjQgMzQyLjY5ODAwMDAwMDAwMDA0IDM4OC4wODIgMzU0LjM5NCAzNzkuNzYzIEwgNDA3LjU4MSA0MDAuNTYzOTk5OTk5OTk5OTYgQyA0MTIuODk5IDQwMi42MzUgNDE4LjIxOCA0MDAuNTYzOTk5OTk5OTk5OTYgNDIwLjM0OCAzOTYuNCBMIDQ2Mi44OTcgMzI0LjYzNSBDIDQ2NS4wMjYgMzIwLjQ4MTk5OTk5OTk5OTk3IDQ2My45NTcgMzE0LjIzNiA0NjAuNzY3IDMxMS4xMTY5OTk5OTk5OTk5NiBMIDQxMy45NjcgMjc2Ljc5OTk5OTk5OTk5OTk1IHogTSAyNTUuNDY4IDMyOC44IEMgMjEzLjk3ODk5OTk5OTk5OTk4IDMyOC44IDE4MS4wMDc5OTk5OTk5OTk5OCAyOTYuNTY1IDE4MS4wMDc5OTk5OTk5OTk5OCAyNTYgQyAxODEuMDA3OTk5OTk5OTk5OTggMjE1LjQzNSAyMTMuOTc4OTk5OTk5OTk5OTggMTgzLjIgMjU1LjQ2Nzk5OTk5OTk5OTk2IDE4My4yIEMgMjk2Ljk1Njk5OTk5OTk5OTk0IDE4My4yIDMyOS45MjkgMjE1LjQzNSAzMjkuOTI5IDI1NiBDIDMyOS45MjkgMjk2LjU2NSAyOTYuOTU3IDMyOC44IDI1NS40Njc5OTk5OTk5OTk5NiAzMjguOCB6IiBzdHJva2UtbGluZWNhcD0icm91bmQiIC8+CjwvZz4KPC9zdmc+ \ No newline at end of file diff --git a/kinode/packages/settings/settings/src/lib.rs b/kinode/packages/settings/settings/src/lib.rs new file mode 100644 index 000000000..805d7a7dc --- /dev/null +++ b/kinode/packages/settings/settings/src/lib.rs @@ -0,0 +1,389 @@ +use kinode_process_lib::{println, *}; +use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, HashSet}; +extern crate base64; + +const ICON: &str = include_str!("icon"); + +#[derive(Debug, Serialize, Deserialize)] +enum SettingsRequest { + Hi { + node: NodeId, + content: String, + timeout: u64, + }, + PeerId(NodeId), + EthConfig(eth::EthConfigAction), + Shutdown, + KillProcess(ProcessId), +} + +type SettingsResponse = Result, SettingsError>; + +#[derive(Debug, Serialize, Deserialize)] +enum SettingsData { + PeerId(net::Identity), +} + +#[derive(Debug, Serialize, Deserialize)] +enum SettingsError { + HiTimeout, + HiOffline, + KernelNonresponsive, + MalformedRequest, + StateFetchFailed, +} + +/// never gets persisted +#[derive(Debug, Serialize, Deserialize)] +struct SettingsState { + pub our: Address, + pub ws_clients: HashSet, + pub identity: Option, + pub diagnostics: Option, + pub eth_rpc_providers: Option, + pub eth_rpc_access_settings: Option, +} + +impl SettingsState { + fn new(our: Address) -> Self { + Self { + our, + ws_clients: HashSet::new(), + identity: None, + diagnostics: None, + eth_rpc_providers: None, + eth_rpc_access_settings: None, + } + } + + fn ws_update(&self) { + for channel in &self.ws_clients { + http::send_ws_push( + *channel, + http::WsMessageType::Text, + LazyLoadBlob { + mime: Some("application/json".to_string()), + bytes: serde_json::to_vec(self).unwrap(), + }, + ); + } + } + + /// get data that the settings page presents to user + /// - get Identity struct from net:distro:sys + /// - get ETH RPC providers from eth:distro:sys + /// - get ETH RPC access settings from eth:distro:sys + /// - get running processes from kernel:distro:sys + fn fetch(&mut self) -> anyhow::Result<()> { + // identity + let Ok(Ok(Message::Response { body, .. })) = Request::to(("our", "net", "distro", "sys")) + .body(rmp_serde::to_vec(&net::NetAction::GetPeer(self.our.node.clone())).unwrap()) + .send_and_await_response(5) + else { + return Err(anyhow::anyhow!("failed to get identity from net")); + }; + let Ok(net::NetResponse::Peer(Some(identity))) = rmp_serde::from_slice(&body) else { + return Err(anyhow::anyhow!("got malformed response from net")); + }; + self.identity = Some(identity); + + // diagnostics string + let Ok(Ok(Message::Response { body, .. })) = Request::to(("our", "net", "distro", "sys")) + .body(rmp_serde::to_vec(&net::NetAction::GetDiagnostics).unwrap()) + .send_and_await_response(5) + else { + return Err(anyhow::anyhow!("failed to get diagnostics from net")); + }; + let Ok(net::NetResponse::Diagnostics(diagnostics_string)) = rmp_serde::from_slice(&body) + else { + return Err(anyhow::anyhow!("got malformed response from net")); + }; + self.diagnostics = Some(diagnostics_string); + + // eth rpc providers + let Ok(Ok(Message::Response { body, .. })) = Request::to(("our", "eth", "distro", "sys")) + .body(serde_json::to_vec(ð::EthConfigAction::GetProviders).unwrap()) + .send_and_await_response(5) + else { + return Err(anyhow::anyhow!("failed to get providers from eth")); + }; + let Ok(eth::EthConfigResponse::Providers(providers)) = serde_json::from_slice(&body) else { + return Err(anyhow::anyhow!("got malformed response from eth")); + }; + self.eth_rpc_providers = Some(providers); + + // eth rpc access settings + let Ok(Ok(Message::Response { body, .. })) = Request::to(("our", "eth", "distro", "sys")) + .body(serde_json::to_vec(ð::EthConfigAction::GetAccessSettings).unwrap()) + .send_and_await_response(5) + else { + return Err(anyhow::anyhow!("failed to get access settings from eth")); + }; + let Ok(eth::EthConfigResponse::AccessSettings(access_settings)) = + serde_json::from_slice(&body) + else { + return Err(anyhow::anyhow!("got malformed response from eth")); + }; + self.eth_rpc_access_settings = Some(access_settings); + + // TODO: running processes + Ok(()) + } +} + +wit_bindgen::generate!({ + path: "wit", + world: "process", +}); + +call_init!(initialize); +fn initialize(our: Address) { + // add ourselves to the homepage + Request::to(("our", "homepage", "homepage", "sys")) + .body( + serde_json::json!({ + "Add": { + "label": "Settings", + "icon": ICON, + "path": "/", // just our root + } + }) + .to_string() + .as_bytes() + .to_vec(), + ) + .send() + .unwrap(); + + // Serve the index.html and other UI files found in pkg/ui at the root path. + http::serve_ui(&our, "ui", true, false, vec!["/"]).unwrap(); + http::bind_http_path("/ask", true, false).unwrap(); + http::bind_ws_path("/", true, false).unwrap(); + + // Grab our state, then enter the main event loop. + let mut state: SettingsState = SettingsState::new(our); + match state.fetch() { + Ok(()) => {} + Err(e) => { + println!("failed to fetch initial state: {e}"); + } + } + main_loop(&mut state); +} + +fn main_loop(state: &mut SettingsState) { + loop { + match await_message() { + Err(send_error) => { + println!("got send error: {send_error:?}"); + continue; + } + Ok(Message::Request { + source, + body, + expects_response, + .. + }) => { + if source.node() != state.our.node { + continue; // ignore messages from other nodes + } + let response = handle_request(&source, &body, state); + if expects_response.is_some() { + Response::new() + .body(serde_json::to_vec(&response).unwrap()) + .send() + .unwrap(); + } + } + _ => continue, // ignore responses + } + } +} + +fn handle_request(source: &Address, body: &[u8], state: &mut SettingsState) -> SettingsResponse { + // source node is ALWAYS ourselves since networking is disabled + if source.process == "http_server:distro:sys" { + // receive HTTP requests and websocket connection messages from our server + match serde_json::from_slice::(body) + .map_err(|_| SettingsError::MalformedRequest)? + { + http::HttpServerRequest::Http(ref incoming) => { + match handle_http_request(state, incoming) { + Ok(()) => Ok(None), + Err(e) => { + println!("error handling HTTP request: {e}"); + http::send_response( + http::StatusCode::INTERNAL_SERVER_ERROR, + None, + "Service Unavailable".to_string().as_bytes().to_vec(), + ); + Ok(None) + } + } + } + http::HttpServerRequest::WebSocketOpen { channel_id, .. } => { + state.ws_clients.insert(channel_id); + Ok(None) + } + http::HttpServerRequest::WebSocketClose(channel_id) => { + // client frontend closed a websocket + state.ws_clients.remove(&channel_id); + Ok(None) + } + http::HttpServerRequest::WebSocketPush { .. } => { + // client frontend sent a websocket message + // we don't expect this! we only use websockets to push updates + Ok(None) + } + } + } else { + let settings_request = serde_json::from_slice::(body) + .map_err(|_| SettingsError::MalformedRequest)?; + handle_settings_request(state, settings_request) + } +} + +/// Handle HTTP requests from our own frontend. +fn handle_http_request( + state: &mut SettingsState, + http_request: &http::IncomingHttpRequest, +) -> anyhow::Result<()> { + match http_request.method()?.as_str() { + "GET" => Ok(http::send_response( + http::StatusCode::OK, + Some(HashMap::from([( + String::from("Content-Type"), + String::from("application/json"), + )])), + serde_json::to_vec(&state)?, + )), + "POST" => { + let Some(blob) = get_blob() else { + return Ok(http::send_response( + http::StatusCode::BAD_REQUEST, + None, + vec![], + )); + }; + let request = serde_json::from_slice::(&blob.bytes)?; + let response = handle_settings_request(state, request); + state.ws_update(); + Ok(http::send_response( + http::StatusCode::OK, + None, + match response { + Ok(Some(data)) => serde_json::to_vec(&data)?, + Ok(None) => "null".as_bytes().to_vec(), + Err(e) => serde_json::to_vec(&e)?, + }, + )) + } + // Any other method will be rejected. + _ => Ok(http::send_response( + http::StatusCode::METHOD_NOT_ALLOWED, + None, + vec![], + )), + } +} + +fn handle_settings_request( + state: &mut SettingsState, + request: SettingsRequest, +) -> SettingsResponse { + match request { + SettingsRequest::Hi { + node, + content, + timeout, + } => { + if let Err(SendError { kind, .. }) = Request::to((&node, "net", "distro", "sys")) + .body(content.into_bytes()) + .send_and_await_response(timeout) + .unwrap() + { + match kind { + SendErrorKind::Timeout => { + println!("message to {node} timed out"); + return Err(SettingsError::HiTimeout); + } + SendErrorKind::Offline => { + println!("{node} is offline or does not exist"); + return Err(SettingsError::HiOffline); + } + } + } else { + return Ok(None); + } + } + SettingsRequest::PeerId(node) => { + // get peer info + match Request::to(("our", "net", "distro", "sys")) + .body(rmp_serde::to_vec(&net::NetAction::GetPeer(node)).unwrap()) + .send_and_await_response(30) + .unwrap() + { + Ok(msg) => match rmp_serde::from_slice::(msg.body()) { + Ok(net::NetResponse::Peer(Some(peer))) => { + println!("got peer info: {peer:?}"); + return Ok(Some(SettingsData::PeerId(peer))); + } + Ok(net::NetResponse::Peer(None)) => { + println!("peer not found"); + return Ok(None); + } + _ => { + return Err(SettingsError::KernelNonresponsive); + } + }, + Err(_) => { + return Err(SettingsError::KernelNonresponsive); + } + } + } + SettingsRequest::EthConfig(action) => { + match Request::to(("our", "eth", "distro", "sys")) + .body(serde_json::to_vec(&action).unwrap()) + .send_and_await_response(30) + .unwrap() + { + Ok(msg) => match serde_json::from_slice::(msg.body()) { + Ok(eth::EthConfigResponse::PermissionDenied) => { + return Err(SettingsError::KernelNonresponsive); + } + Ok(other) => { + println!("eth config action succeeded: {other:?}"); + } + Err(_) => { + return Err(SettingsError::KernelNonresponsive); + } + }, + Err(_) => { + return Err(SettingsError::KernelNonresponsive); + } + } + } + SettingsRequest::Shutdown => { + // shutdown the node IMMEDIATELY! + Request::to(("our", "kernel", "distro", "sys")) + .body(serde_json::to_vec(&kernel_types::KernelCommand::Shutdown).unwrap()) + .send() + .unwrap(); + } + SettingsRequest::KillProcess(pid) => { + // kill a process + if let Err(_) = Request::to(("our", "kernel", "distro", "sys")) + .body(serde_json::to_vec(&kernel_types::KernelCommand::KillProcess(pid)).unwrap()) + .send_and_await_response(30) + .unwrap() + { + return SettingsResponse::Err(SettingsError::KernelNonresponsive); + } + } + } + + state.fetch().map_err(|_| SettingsError::StateFetchFailed)?; + state.ws_update(); + SettingsResponse::Ok(None) +} diff --git a/kinode/src/kernel/mod.rs b/kinode/src/kernel/mod.rs index 91d1be789..2635cda87 100644 --- a/kinode/src/kernel/mod.rs +++ b/kinode/src/kernel/mod.rs @@ -75,6 +75,7 @@ async fn persist_state( } /// handle commands inside messages sent directly to kernel. source is always our own node. +/// returns Some(()) if the kernel should shut down. async fn handle_kernel_request( our_name: String, keypair: Arc, @@ -88,9 +89,9 @@ async fn handle_kernel_request( caps_oracle: t::CapMessageSender, engine: &Engine, home_directory_path: &str, -) { +) -> Option<()> { let t::Message::Request(request) = km.message else { - return; + return None; }; let command: t::KernelCommand = match serde_json::from_slice(&request.body) { Err(e) => { @@ -100,7 +101,7 @@ async fn handle_kernel_request( content: format!("kernel: couldn't parse command: {:?}", e), }) .await; - return; + return None; } Ok(c) => c, }; @@ -138,6 +139,7 @@ async fn handle_kernel_request( for handle in process_handles.values() { handle.abort(); } + return Some(()); } // // initialize a new process. this is the only way to create a new process. @@ -183,7 +185,7 @@ async fn handle_kernel_request( }) .await .expect("event loop: fatal: sender died"); - return; + return None; }; // check cap sigs & transform valid to unsigned to be plugged into procs @@ -321,7 +323,7 @@ async fn handle_kernel_request( ), }) .await; - return; + return None; }; let signed_caps: Vec<(t::Capability, Vec)> = capabilities .iter() @@ -361,7 +363,7 @@ async fn handle_kernel_request( ), }) .await; - return; + return None; }; for cap in capabilities { entry.capabilities.remove(&cap); @@ -465,7 +467,7 @@ async fn handle_kernel_request( content: format!("kernel: no such process {process_id} to kill"), }) .await; - return; + return None; } }; process_handle.abort(); @@ -484,7 +486,7 @@ async fn handle_kernel_request( content: format!("killing process {process_id}"), }) .await; - return; + return None; } let _ = send_to_terminal .send(t::Printout { @@ -537,7 +539,7 @@ async fn handle_kernel_request( content: format!("kernel: no such running process {}", process_id), }) .await; - return; + return None; }; let _ = send_to_terminal .send(t::Printout { @@ -563,6 +565,7 @@ async fn handle_kernel_request( } }, } + None } /// spawn a process loop and insert the process in the relevant kernel state maps @@ -1064,7 +1067,7 @@ pub async fn kernel( if our.name != kernel_message.source.node { continue; } - handle_kernel_request( + if let Some(()) = handle_kernel_request( our.name.clone(), keypair.clone(), kernel_message, @@ -1077,7 +1080,10 @@ pub async fn kernel( caps_oracle_sender.clone(), &engine, &home_directory_path, - ).await; + ).await { + // shut down the node + return Ok(()); + } } else { // pass message to appropriate runtime module or process match senders.get(&kernel_message.target.process) { diff --git a/kinode/src/main.rs b/kinode/src/main.rs index b0319ec36..05f1f97d1 100644 --- a/kinode/src/main.rs +++ b/kinode/src/main.rs @@ -457,12 +457,15 @@ async fn main() { // if a runtime task exits, try to recover it, // unless it was terminal signaling a quit // or a SIG* was intercepted - let mut quit_msg: String = tokio::select! { + let quit_msg: String = tokio::select! { Some(Ok(res)) = tasks.join_next() => { - format!( - "uh oh, a kernel process crashed -- this should never happen: {:?}", - res - ) + match res { + Ok(_) => "graceful exit".into(), + Err(e) => format!( + "uh oh, a kernel process crashed -- this should never happen: {e:?}" + ), + } + } quit = terminal::terminal( our.clone(), @@ -476,39 +479,39 @@ async fn main() { verbose_mode, ) => { match quit { - Ok(_) => "graceful exit".into(), + Ok(_) => match kernel_message_sender + .send(KernelMessage { + id: rand::random(), + source: Address { + node: our.name.clone(), + process: KERNEL_PROCESS_ID.clone(), + }, + target: Address { + node: our.name.clone(), + process: KERNEL_PROCESS_ID.clone(), + }, + rsvp: None, + message: Message::Request(Request { + inherit: false, + expects_response: None, + body: serde_json::to_vec(&KernelCommand::Shutdown).unwrap(), + metadata: None, + capabilities: vec![], + }), + lazy_load_blob: None, + }) + .await + { + Ok(()) => "graceful exit".into(), + Err(_) => { + "failed to gracefully shut down kernel".into() + } + }, Err(e) => e.to_string(), } } }; - // gracefully abort all running processes in kernel - if let Err(_) = kernel_message_sender - .send(KernelMessage { - id: rand::random(), - source: Address { - node: our.name.clone(), - process: KERNEL_PROCESS_ID.clone(), - }, - target: Address { - node: our.name.clone(), - process: KERNEL_PROCESS_ID.clone(), - }, - rsvp: None, - message: Message::Request(Request { - inherit: false, - expects_response: None, - body: serde_json::to_vec(&KernelCommand::Shutdown).unwrap(), - metadata: None, - capabilities: vec![], - }), - lazy_load_blob: None, - }) - .await - { - quit_msg = "failed to gracefully shut down kernel".into(); - } - // abort all remaining tasks tasks.shutdown().await; let stdout = std::io::stdout();