From 02eb744585a77fa5352c15d90260470d2cc5d767 Mon Sep 17 00:00:00 2001 From: Matt Gabrenya Date: Tue, 20 Aug 2024 15:03:38 -0700 Subject: [PATCH] refactor: extract holochain management functionality into standalone crate 'holochain-manager', modify tauri-plugin-holochain to use holochain-manager under-the-hood --- Cargo.lock | 78 +++-- crates/hc-pilot/Cargo.toml | 1 + .../hc-pilot/gen/schemas/acl-manifests.json | 2 +- .../hc-pilot/gen/schemas/desktop-schema.json | 15 - crates/hc-pilot/gen/schemas/linux-schema.json | 15 - crates/hc-pilot/src/main.rs | 7 +- crates/holochain-manager/Cargo.toml | 63 ++++ .../src/commands/install_web_app.rs | 270 +++++++++++++++ .../src/commands/list_apps.rs | 15 + crates/holochain-manager/src/commands/mod.rs | 8 + .../src/commands/sign_zome_call.rs | 108 ++++++ .../src/config.rs | 0 crates/holochain-manager/src/error.rs | 81 +++++ crates/holochain-manager/src/features.rs | 15 + .../src/filesystem.rs | 0 crates/holochain-manager/src/holochain.udl | 21 ++ crates/holochain-manager/src/http_server.rs | 56 ++++ .../src/lair_signer.rs | 0 .../src/launch.rs | 4 +- .../src/launch/mdns.rs | 85 +++-- .../src/launch/signal.rs | 0 crates/holochain-manager/src/lib.rs | 314 ++++++++++++++++++ crates/tauri-plugin-holochain/Cargo.toml | 46 +-- crates/tauri-plugin-holochain/build.rs | 2 - crates/tauri-plugin-holochain/src/commands.rs | 2 +- .../src/commands/install_web_app.rs | 275 +-------------- .../src/commands/list_apps.rs | 9 +- .../src/commands/sign_zome_call.rs | 110 +----- crates/tauri-plugin-holochain/src/error.rs | 37 +-- .../tauri-plugin-holochain/src/http_server.rs | 200 ----------- crates/tauri-plugin-holochain/src/lib.rs | 262 ++------------- 31 files changed, 1094 insertions(+), 1007 deletions(-) create mode 100644 crates/holochain-manager/Cargo.toml create mode 100644 crates/holochain-manager/src/commands/install_web_app.rs create mode 100644 crates/holochain-manager/src/commands/list_apps.rs create mode 100644 crates/holochain-manager/src/commands/mod.rs create mode 100644 crates/holochain-manager/src/commands/sign_zome_call.rs rename crates/{tauri-plugin-holochain => holochain-manager}/src/config.rs (100%) create mode 100644 crates/holochain-manager/src/error.rs create mode 100644 crates/holochain-manager/src/features.rs rename crates/{tauri-plugin-holochain => holochain-manager}/src/filesystem.rs (100%) create mode 100644 crates/holochain-manager/src/holochain.udl create mode 100644 crates/holochain-manager/src/http_server.rs rename crates/{tauri-plugin-holochain => holochain-manager}/src/lair_signer.rs (100%) rename crates/{tauri-plugin-holochain => holochain-manager}/src/launch.rs (98%) rename crates/{tauri-plugin-holochain => holochain-manager}/src/launch/mdns.rs (66%) rename crates/{tauri-plugin-holochain => holochain-manager}/src/launch/signal.rs (100%) create mode 100644 crates/holochain-manager/src/lib.rs delete mode 100644 crates/tauri-plugin-holochain/src/http_server.rs diff --git a/Cargo.lock b/Cargo.lock index 02052b29..152dcd55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3056,6 +3056,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap 4.5.13", + "holochain-manager", "holochain_client", "holochain_types", "lair_keystore", @@ -3375,6 +3376,51 @@ dependencies = [ "wasmer-middlewares", ] +[[package]] +name = "holochain-manager" +version = "0.0.0" +dependencies = [ + "anyhow", + "async-std", + "async-trait", + "base64 0.22.1", + "bzip2", + "either", + "futures", + "hc_seed_bundle", + "holochain", + "holochain_client", + "holochain_conductor_api", + "holochain_keystore", + "holochain_types", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "kitsune_p2p_mdns", + "kitsune_p2p_types", + "lair_keystore", + "lair_keystore_api", + "local-ip-address", + "log", + "mime_guess", + "mr_bundle", + "nanoid", + "one_err", + "portpicker", + "serde", + "serde_json", + "sha256", + "symlink", + "thiserror", + "tls-listener", + "tokio", + "tx5-signal", + "tx5-signal-srv", + "url", + "url2", + "zip 0.6.6", +] + [[package]] name = "holochain_cascade" version = "0.3.2" @@ -8908,47 +8954,17 @@ dependencies = [ name = "tauri-plugin-holochain" version = "0.0.0" dependencies = [ - "anyhow", - "async-std", - "async-trait", - "base64 0.22.1", - "bzip2", - "either", - "futures", "hc_seed_bundle", - "holochain", + "holochain-manager", "holochain_client", - "holochain_conductor_api", - "holochain_keystore", "holochain_types", - "http-body-util", - "hyper 1.4.1", - "hyper-util", - "kitsune_p2p_mdns", - "kitsune_p2p_types", - "lair_keystore", - "lair_keystore_api", - "local-ip-address", "log", - "mime_guess", - "mr_bundle", - "nanoid", "one_err", - "portpicker", "serde", - "serde_json", - "sha256", - "symlink", "tauri", "tauri-plugin", "thiserror", - "tls-listener", - "tokio", - "tx5-signal", - "tx5-signal-srv", "url", - "url2", - "zip 0.6.6", ] [[package]] diff --git a/crates/hc-pilot/Cargo.toml b/crates/hc-pilot/Cargo.toml index 110c2e94..88e51fad 100644 --- a/crates/hc-pilot/Cargo.toml +++ b/crates/hc-pilot/Cargo.toml @@ -10,6 +10,7 @@ tauri = "2.0.0-rc" tauri-plugin-log = "2.0.0-rc" tauri-plugin-holochain = { path = "../tauri-plugin-holochain" } +holochain-manager = { path = "../holochain-manager" } holochain_types = "0.3.2" holochain_client = "0.5.1" diff --git a/crates/hc-pilot/gen/schemas/acl-manifests.json b/crates/hc-pilot/gen/schemas/acl-manifests.json index 4be4f77f..35c3aeb6 100644 --- a/crates/hc-pilot/gen/schemas/acl-manifests.json +++ b/crates/hc-pilot/gen/schemas/acl-manifests.json @@ -1 +1 @@ -{"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"holochain":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin","permissions":[]},"permissions":{"allow-get-locales":{"identifier":"allow-get-locales","description":"Enables the get_locales command without any pre-configured scope.","commands":{"allow":["get_locales"],"deny":[]}},"allow-get-runtime-info":{"identifier":"allow-get-runtime-info","description":"Enables the get_runtime_info command without any pre-configured scope.","commands":{"allow":["get_runtime_info"],"deny":[]}},"allow-is-holochain-ready":{"identifier":"allow-is-holochain-ready","description":"Enables the is_holochain_ready command without any pre-configured scope.","commands":{"allow":["is_holochain_ready"],"deny":[]}},"allow-list-apps":{"identifier":"allow-list-apps","description":"Enables the list_apps command without any pre-configured scope.","commands":{"allow":["list_apps"],"deny":[]}},"allow-open-app":{"identifier":"allow-open-app","description":"Enables the open_app command without any pre-configured scope.","commands":{"allow":["open_app"],"deny":[]}},"allow-sign-zome-call":{"identifier":"allow-sign-zome-call","description":"Enables the sign_zome_call command without any pre-configured scope.","commands":{"allow":["sign_zome_call"],"deny":[]}},"deny-get-locales":{"identifier":"deny-get-locales","description":"Denies the get_locales command without any pre-configured scope.","commands":{"allow":[],"deny":["get_locales"]}},"deny-get-runtime-info":{"identifier":"deny-get-runtime-info","description":"Denies the get_runtime_info command without any pre-configured scope.","commands":{"allow":[],"deny":["get_runtime_info"]}},"deny-is-holochain-ready":{"identifier":"deny-is-holochain-ready","description":"Denies the is_holochain_ready command without any pre-configured scope.","commands":{"allow":[],"deny":["is_holochain_ready"]}},"deny-list-apps":{"identifier":"deny-list-apps","description":"Denies the list_apps command without any pre-configured scope.","commands":{"allow":[],"deny":["list_apps"]}},"deny-open-app":{"identifier":"deny-open-app","description":"Denies the open_app command without any pre-configured scope.","commands":{"allow":[],"deny":["open_app"]}},"deny-sign-zome-call":{"identifier":"deny-sign-zome-call","description":"Denies the sign_zome_call command without any pre-configured scope.","commands":{"allow":[],"deny":["sign_zome_call"]}}},"permission_sets":{},"global_scope_schema":null},"log":{"default_permission":{"identifier":"default","description":"Allows the log command","permissions":["allow-log"]},"permissions":{"allow-log":{"identifier":"allow-log","description":"Enables the log command without any pre-configured scope.","commands":{"allow":["log"],"deny":[]}},"deny-log":{"identifier":"deny-log","description":"Denies the log command without any pre-configured scope.","commands":{"allow":[],"deny":["log"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file +{"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"holochain":{"default_permission":null,"permissions":{"allow-get-locales":{"identifier":"allow-get-locales","description":"Enables the get_locales command without any pre-configured scope.","commands":{"allow":["get_locales"],"deny":[]}},"allow-is-holochain-ready":{"identifier":"allow-is-holochain-ready","description":"Enables the is_holochain_ready command without any pre-configured scope.","commands":{"allow":["is_holochain_ready"],"deny":[]}},"allow-list-apps":{"identifier":"allow-list-apps","description":"Enables the list_apps command without any pre-configured scope.","commands":{"allow":["list_apps"],"deny":[]}},"allow-open-app":{"identifier":"allow-open-app","description":"Enables the open_app command without any pre-configured scope.","commands":{"allow":["open_app"],"deny":[]}},"allow-sign-zome-call":{"identifier":"allow-sign-zome-call","description":"Enables the sign_zome_call command without any pre-configured scope.","commands":{"allow":["sign_zome_call"],"deny":[]}},"deny-get-locales":{"identifier":"deny-get-locales","description":"Denies the get_locales command without any pre-configured scope.","commands":{"allow":[],"deny":["get_locales"]}},"deny-is-holochain-ready":{"identifier":"deny-is-holochain-ready","description":"Denies the is_holochain_ready command without any pre-configured scope.","commands":{"allow":[],"deny":["is_holochain_ready"]}},"deny-list-apps":{"identifier":"deny-list-apps","description":"Denies the list_apps command without any pre-configured scope.","commands":{"allow":[],"deny":["list_apps"]}},"deny-open-app":{"identifier":"deny-open-app","description":"Denies the open_app command without any pre-configured scope.","commands":{"allow":[],"deny":["open_app"]}},"deny-sign-zome-call":{"identifier":"deny-sign-zome-call","description":"Denies the sign_zome_call command without any pre-configured scope.","commands":{"allow":[],"deny":["sign_zome_call"]}}},"permission_sets":{},"global_scope_schema":null},"log":{"default_permission":{"identifier":"default","description":"Allows the log command","permissions":["allow-log"]},"permissions":{"allow-log":{"identifier":"allow-log","description":"Enables the log command without any pre-configured scope.","commands":{"allow":["log"],"deny":[]}},"deny-log":{"identifier":"deny-log","description":"Denies the log command without any pre-configured scope.","commands":{"allow":[],"deny":["log"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file diff --git a/crates/hc-pilot/gen/schemas/desktop-schema.json b/crates/hc-pilot/gen/schemas/desktop-schema.json index b1088f1d..9fa6cd53 100644 --- a/crates/hc-pilot/gen/schemas/desktop-schema.json +++ b/crates/hc-pilot/gen/schemas/desktop-schema.json @@ -2111,7 +2111,6 @@ ] }, { - "description": "holochain:default -> Default permissions for the plugin", "type": "string", "enum": [ "holochain:default" @@ -2124,13 +2123,6 @@ "holochain:allow-get-locales" ] }, - { - "description": "holochain:allow-get-runtime-info -> Enables the get_runtime_info command without any pre-configured scope.", - "type": "string", - "enum": [ - "holochain:allow-get-runtime-info" - ] - }, { "description": "holochain:allow-is-holochain-ready -> Enables the is_holochain_ready command without any pre-configured scope.", "type": "string", @@ -2166,13 +2158,6 @@ "holochain:deny-get-locales" ] }, - { - "description": "holochain:deny-get-runtime-info -> Denies the get_runtime_info command without any pre-configured scope.", - "type": "string", - "enum": [ - "holochain:deny-get-runtime-info" - ] - }, { "description": "holochain:deny-is-holochain-ready -> Denies the is_holochain_ready command without any pre-configured scope.", "type": "string", diff --git a/crates/hc-pilot/gen/schemas/linux-schema.json b/crates/hc-pilot/gen/schemas/linux-schema.json index b1088f1d..9fa6cd53 100644 --- a/crates/hc-pilot/gen/schemas/linux-schema.json +++ b/crates/hc-pilot/gen/schemas/linux-schema.json @@ -2111,7 +2111,6 @@ ] }, { - "description": "holochain:default -> Default permissions for the plugin", "type": "string", "enum": [ "holochain:default" @@ -2124,13 +2123,6 @@ "holochain:allow-get-locales" ] }, - { - "description": "holochain:allow-get-runtime-info -> Enables the get_runtime_info command without any pre-configured scope.", - "type": "string", - "enum": [ - "holochain:allow-get-runtime-info" - ] - }, { "description": "holochain:allow-is-holochain-ready -> Enables the is_holochain_ready command without any pre-configured scope.", "type": "string", @@ -2166,13 +2158,6 @@ "holochain:deny-get-locales" ] }, - { - "description": "holochain:deny-get-runtime-info -> Denies the get_runtime_info command without any pre-configured scope.", - "type": "string", - "enum": [ - "holochain:deny-get-runtime-info" - ] - }, { "description": "holochain:deny-is-holochain-ready -> Denies the is_holochain_ready command without any pre-configured scope.", "type": "string", diff --git a/crates/hc-pilot/src/main.rs b/crates/hc-pilot/src/main.rs index c6e91445..2003cf24 100644 --- a/crates/hc-pilot/src/main.rs +++ b/crates/hc-pilot/src/main.rs @@ -9,7 +9,8 @@ use lair_keystore::dependencies::sodoken::{BufRead, BufWrite}; use std::collections::HashMap; use std::path::PathBuf; use tauri::{AppHandle, Context, Wry}; -use tauri_plugin_holochain::{HolochainExt, HolochainPluginConfig, WANNetworkConfig}; +use tauri_plugin_holochain::HolochainExt; +use holochain_manager::{HolochainManagerConfig, WANNetworkConfig}; use url2::url2; #[derive(Parser, Debug)] @@ -105,7 +106,7 @@ fn main() { ) .plugin(tauri_plugin_holochain::init( vec_to_locked(password.as_bytes().to_vec()).expect("Can't build passphrase"), - HolochainPluginConfig { + HolochainManagerConfig { wan_network_config, holochain_dir: conductor_dir, }, @@ -157,7 +158,7 @@ async fn setup( membrane_proofs: HashMap>, network_seed: Option, ) -> anyhow::Result { - let admin_ws = handle.holochain()?.admin_websocket().await?; + let admin_ws = handle.holochain()?.holochain_runtime.admin_websocket().await?; let agent_key = match agent_key { Some(agent_key) => agent_key, None => { diff --git a/crates/holochain-manager/Cargo.toml b/crates/holochain-manager/Cargo.toml new file mode 100644 index 00000000..2e8b737e --- /dev/null +++ b/crates/holochain-manager/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "holochain-manager" +version = "0.0.0" +authors = ["guillem@darksoil.studio"] +description = "" +edition = "2021" +rust-version = "1.75" + +[dependencies] +# Holochain dependencies +mr_bundle = "0.3.2" +holochain = "0.3.2" +holochain_types = "0.3.2" +holochain_keystore = "0.3.2" +holochain_conductor_api = "0.3.2" + +kitsune_p2p_mdns = "0.3.2" +kitsune_p2p_types = "0.3.2" + +tx5-signal-srv = "0.0.14-alpha" +tx5-signal = "0.0.14-alpha" + +# Lair dependencies +hc_seed_bundle = "0.2" +lair_keystore = "0.4.5" +lair_keystore_api = "0.4.5" + +# Holochain client +holochain_client = "0.5.1" + +local-ip-address = "0.6" +serde_json = "1" +sha256 = "1" +log = "0.4" +symlink = "0.1.0" +nanoid = "0.4.0" +async-std = "1.12" +serde = "1.0.193" +thiserror = "1.0" +url = "2.4.0" +url2 = "0.0.6" +zip = { version = "0.6" } +bzip2 = { version = "0.4", features = ["static"] } +portpicker = "0.1" +mime_guess = "2.0.4" +hyper = { version = "1", features = ["full"] } +hyper-util = { version = "0.1", features = ["full"] } +http-body-util = "0.1" +tls-listener = "0.8" +futures = "0.3" +either = "*" +tokio = "1" +one_err = "0" +base64 = "0.22" +anyhow = "1" +async-trait = "0.1" + +[features] +default = ["gossip_arc_normal"] + +gossip_arc_empty = [] +gossip_arc_full = [] +gossip_arc_normal = [] \ No newline at end of file diff --git a/crates/holochain-manager/src/commands/install_web_app.rs b/crates/holochain-manager/src/commands/install_web_app.rs new file mode 100644 index 00000000..36d50eff --- /dev/null +++ b/crates/holochain-manager/src/commands/install_web_app.rs @@ -0,0 +1,270 @@ +use std::{ + collections::{BTreeMap, HashMap}, + path::PathBuf, +}; + +use holochain::prelude::{ + AppBundle, AppBundleError, AppBundleSource, AppManifest, CoordinatorBundle, + CoordinatorManifest, DnaBundle, DnaError, DnaFile, DnaHash, MembraneProof, NetworkSeed, + RoleName, UpdateCoordinatorsPayload, ZomeDependency, ZomeError, ZomeLocation, ZomeManifest, +}; +use holochain_client::{ + AdminWebsocket, AgentPubKey, AppInfo, ConductorApiError, InstallAppPayload, InstalledAppId, +}; +use holochain_conductor_api::{AppInfoStatus, CellInfo}; +use holochain_types::web_app::WebAppBundle; +use mr_bundle::{error::MrBundleError, Bundle, ResourceBytes}; + +use crate::filesystem::FileSystemError; + +pub async fn install_web_app( + admin_ws: &AdminWebsocket, + app_id: String, + bundle: WebAppBundle, + membrane_proofs: HashMap, + agent: Option, + network_seed: Option, +) -> crate::Result { + let app_info = install_app( + admin_ws, + app_id.clone(), + bundle.happ_bundle().await?, + membrane_proofs, + agent, + network_seed, + ) + .await?; + + log::info!("Installed web-app's ui {app_id:?}"); + + Ok(app_info) +} + +pub async fn install_app( + admin_ws: &AdminWebsocket, + app_id: String, + bundle: AppBundle, + membrane_proofs: HashMap, + agent: Option, + network_seed: Option, +) -> crate::Result { + log::info!("Installing app {}", app_id); + + let agent_key = match agent { + Some(agent) => agent, + None => admin_ws + .generate_agent_pub_key() + .await + .map_err(|err| crate::Error::ConductorApiError(err))?, + }; + + let app_info = admin_ws + .install_app(InstallAppPayload { + agent_key, + membrane_proofs, + network_seed, + source: AppBundleSource::Bundle(bundle), + installed_app_id: Some(app_id.clone()), + }) + .await + .map_err(|err| crate::Error::ConductorApiError(err))?; + log::info!("Installed app {app_info:?}"); + + let response = admin_ws + .enable_app(app_id.clone()) + .await + .map_err(|err| crate::Error::ConductorApiError(err))?; + + log::info!("Enabled app {app_id:?}"); + + Ok(response.app) +} + +#[derive(Debug, thiserror::Error)] +pub enum UpdateAppError { + #[error(transparent)] + AppBundleError(#[from] AppBundleError), + + #[error(transparent)] + ZomeError(#[from] ZomeError), + + #[error(transparent)] + MrBundleError(#[from] MrBundleError), + + #[error(transparent)] + FileSystemError(#[from] FileSystemError), + + #[error(transparent)] + DnaError(#[from] DnaError), + + #[error("ConductorApiError: `{0:?}`")] + ConductorApiError(ConductorApiError), + + #[error("Error connecting to the websocket")] + WebsocketError, + + #[error("The given app was not found: {0}")] + AppNotFound(String), + + #[error("The role {0} was not found the app {1}")] + RoleNotFound(RoleName, InstalledAppId), +} + +pub async fn update_app( + admin_ws: &AdminWebsocket, + app_id: String, + bundle: AppBundle, +) -> Result<(), UpdateAppError> { + log::info!( + "Checking whether the coordinator zomes for app {} need to be updated", + app_id + ); + + // Get the DNA def from the admin websocket + let apps = admin_ws + .list_apps(None) + .await + .map_err(|err| UpdateAppError::ConductorApiError(err))?; + + let mut app = apps + .into_iter() + .find(|app| app.installed_app_id.eq(&app_id)) + .ok_or(UpdateAppError::AppNotFound(app_id.clone()))?; + + let new_dna_files = resolve_dna_files(bundle).await?; + + let mut updated = false; + + for (role_name, new_dna_file) in new_dna_files { + let cells = app + .cell_info + .remove(&role_name) + .ok_or(UpdateAppError::RoleNotFound( + role_name.clone(), + app.installed_app_id.clone(), + ))?; + + for cell in cells { + let mut zomes: Vec = Vec::new(); + let mut resources: Vec<(PathBuf, ResourceBytes)> = Vec::new(); + + let dna_hash = match cell { + CellInfo::Provisioned(c) => c.cell_id.dna_hash().clone(), + CellInfo::Cloned(c) => c.cell_id.dna_hash().clone(), + CellInfo::Stem(c) => c.original_dna_hash.clone(), + }; + let old_dna_def = admin_ws + .get_dna_definition(dna_hash.clone()) + .await + .map_err(|err| UpdateAppError::ConductorApiError(err))?; + + for (zome_name, coordinator_zome) in new_dna_file.dna_def().coordinator_zomes.iter() { + let deps = coordinator_zome + .clone() + .erase_type() + .dependencies() + .to_vec(); + let dependencies = deps + .into_iter() + .map(|name| ZomeDependency { name }) + .collect(); + + if let Some(old_zome_def) = old_dna_def + .coordinator_zomes + .iter() + .find(|(zome, _)| zome.eq(&zome_name)) + { + if !old_zome_def + .1 + .wasm_hash(&zome_name)? + .eq(&coordinator_zome.wasm_hash(&zome_name)?) + { + log::info!("Updating coordinator zome {zome_name} for role {role_name}"); + let resource_path = PathBuf::from(zome_name.0.to_string()); + zomes.push(ZomeManifest { + name: zome_name.clone(), + hash: None, + dylib: None, + location: ZomeLocation::Bundled(resource_path.clone()), + dependencies: Some(dependencies), + }); + let wasm = new_dna_file.get_wasm_for_zome(&zome_name)?; + resources.push((resource_path, wasm.code().to_vec().into())); + } + } else { + log::info!("Adding new coordinator zome {zome_name} for role {role_name}"); + let resource_path = PathBuf::from(zome_name.0.to_string()); + zomes.push(ZomeManifest { + name: zome_name.clone(), + hash: None, + dylib: None, + location: ZomeLocation::Bundled(resource_path.clone()), + dependencies: Some(dependencies), + }); + let wasm = new_dna_file.get_wasm_for_zome(&zome_name)?; + resources.push((resource_path, wasm.code().to_vec().into())); + } + } + + if !zomes.is_empty() { + let source: CoordinatorBundle = + Bundle::new(CoordinatorManifest { zomes }, resources, PathBuf::from("/"))? + .into(); + let req = UpdateCoordinatorsPayload { + dna_hash, + source: holochain_types::prelude::CoordinatorSource::Bundle(Box::new(source)), + }; + + admin_ws + .update_coordinators(req) + .await + .map_err(|err| UpdateAppError::ConductorApiError(err))?; + updated = true; + } + } + } + + if updated { + if let AppInfoStatus::Running = app.status { + admin_ws + .disable_app(app_id.clone()) + .await + .map_err(|err| UpdateAppError::ConductorApiError(err))?; + admin_ws + .enable_app(app_id.clone()) + .await + .map_err(|err| UpdateAppError::ConductorApiError(err))?; + } + log::info!("Updated app {app_id:?}"); + } + + Ok(()) +} + +async fn resolve_dna_files( + app_bundle: AppBundle, +) -> Result, UpdateAppError> { + let mut dna_files: BTreeMap = BTreeMap::new(); + + let bundle = app_bundle.into_inner(); + + for app_role in bundle.manifest().app_roles() { + if let Some(location) = app_role.dna.location { + let (dna_def, _) = resolve_location(&bundle, &location).await?; + + dna_files.insert(app_role.name.clone(), dna_def); + } + } + + Ok(dna_files) +} + +async fn resolve_location( + app_bundle: &Bundle, + location: &mr_bundle::Location, +) -> Result<(DnaFile, DnaHash), UpdateAppError> { + let bytes = app_bundle.resolve(location).await?; + let dna_bundle: DnaBundle = mr_bundle::Bundle::decode(&bytes)?.into(); + let (dna_file, original_hash) = dna_bundle.into_dna_file(Default::default()).await?; + Ok((dna_file, original_hash)) +} diff --git a/crates/holochain-manager/src/commands/list_apps.rs b/crates/holochain-manager/src/commands/list_apps.rs new file mode 100644 index 00000000..af294a6a --- /dev/null +++ b/crates/holochain-manager/src/commands/list_apps.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use crate::HolochainRuntime; +use holochain_client::AppInfo; + +pub async fn list_apps(holochain: Arc) -> crate::Result> { + let admin_ws = holochain.admin_websocket().await?; + + let apps = admin_ws + .list_apps(None) + .await + .map_err(|err| crate::Error::ConductorApiError(err))?; + + Ok(apps) +} diff --git a/crates/holochain-manager/src/commands/mod.rs b/crates/holochain-manager/src/commands/mod.rs new file mode 100644 index 00000000..873ed3c5 --- /dev/null +++ b/crates/holochain-manager/src/commands/mod.rs @@ -0,0 +1,8 @@ +mod install_web_app; +pub use install_web_app::{UpdateAppError, update_app, install_app, install_web_app}; + +mod list_apps; +pub use list_apps::list_apps; + +mod sign_zome_call; +pub use sign_zome_call::{sign_zome_call, sign_zome_call_with_client, ZomeCallUnsignedTauri}; diff --git a/crates/holochain-manager/src/commands/sign_zome_call.rs b/crates/holochain-manager/src/commands/sign_zome_call.rs new file mode 100644 index 00000000..fa52218a --- /dev/null +++ b/crates/holochain-manager/src/commands/sign_zome_call.rs @@ -0,0 +1,108 @@ +use std::sync::Arc; +use holochain::{ + conductor::api::ZomeCall, + prelude::{CapSecret, CellId, ExternIO, FunctionName, Timestamp, ZomeCallUnsigned, ZomeName}, +}; +use holochain_client::AgentPubKey; +use holochain_types::prelude::Signature; +use lair_keystore_api::LairClient; +use serde::Deserialize; +use crate::HolochainRuntime; + +pub async fn sign_zome_call( + holochain: Arc, + zome_call_unsigned: ZomeCallUnsignedTauri, +) -> crate::Result { + let zome_call_unsigned_converted: ZomeCallUnsigned = zome_call_unsigned.into(); + + let signed_zome_call = sign_zome_call_with_client( + zome_call_unsigned_converted, + &holochain + .conductor_handle + .keystore() + .lair_client() + ) + .await?; + + Ok(signed_zome_call) +} + +/// Signs an unsigned zome call with the given LairClient +pub async fn sign_zome_call_with_client( + zome_call_unsigned: ZomeCallUnsigned, + client: &LairClient, +) -> crate::Result { + // sign the zome call + let pub_key = zome_call_unsigned.provenance.clone(); + let mut pub_key_2 = [0; 32]; + pub_key_2.copy_from_slice(pub_key.get_raw_32()); + + let data_to_sign = zome_call_unsigned.data_to_sign()?; + + let sig = client + .sign_by_pub_key(pub_key_2.into(), None, data_to_sign) + .await + .map_err(|err| crate::Error::LairError(err))?; + + let signature = Signature(*sig.0); + + let signed_zome_call = ZomeCall { + cell_id: zome_call_unsigned.cell_id, + zome_name: zome_call_unsigned.zome_name, + fn_name: zome_call_unsigned.fn_name, + payload: zome_call_unsigned.payload, + cap_secret: zome_call_unsigned.cap_secret, + provenance: zome_call_unsigned.provenance, + nonce: zome_call_unsigned.nonce, + expires_at: zome_call_unsigned.expires_at, + signature, + }; + + return Ok(signed_zome_call); +} + +/// The version of an unsigned zome call that's compatible with the serialization +/// behavior of tauri's IPC channel (serde serialization) +/// nonce is a byte array [u8, 32] because holochain's nonce type seems to +/// have "non-serde" deserialization behavior. +#[derive(Deserialize, Debug)] +pub struct ZomeCallUnsignedTauri { + pub provenance: AgentPubKey, + pub cell_id: CellId, + pub zome_name: ZomeName, + pub fn_name: FunctionName, + pub cap_secret: Option, + pub payload: ExternIO, + pub nonce: [u8; 32], + pub expires_at: Timestamp, +} + +impl Into for ZomeCallUnsignedTauri { + fn into(self) -> ZomeCallUnsigned { + ZomeCallUnsigned { + provenance: self.provenance, + cell_id: self.cell_id, + zome_name: self.zome_name, + fn_name: self.fn_name, + cap_secret: self.cap_secret, + payload: self.payload, + nonce: self.nonce.into(), + expires_at: self.expires_at, + } + } +} + +impl Clone for ZomeCallUnsignedTauri { + fn clone(&self) -> Self { + ZomeCallUnsignedTauri { + provenance: self.provenance.clone(), + cell_id: self.cell_id.clone(), + zome_name: self.zome_name.clone(), + fn_name: self.fn_name.clone(), + cap_secret: self.cap_secret.clone(), + payload: self.payload.clone(), + nonce: self.nonce.clone(), + expires_at: self.expires_at.clone(), + } + } +} diff --git a/crates/tauri-plugin-holochain/src/config.rs b/crates/holochain-manager/src/config.rs similarity index 100% rename from crates/tauri-plugin-holochain/src/config.rs rename to crates/holochain-manager/src/config.rs diff --git a/crates/holochain-manager/src/error.rs b/crates/holochain-manager/src/error.rs new file mode 100644 index 00000000..57b313f4 --- /dev/null +++ b/crates/holochain-manager/src/error.rs @@ -0,0 +1,81 @@ +use holochain::{conductor::error::ConductorError, prelude::SerializedBytesError}; +use holochain_client::ConductorApiError; +use mr_bundle::error::MrBundleError; +use one_err::OneErr; +use serde::{ser::Serializer, Serialize}; + +use crate::{commands::UpdateAppError, filesystem::FileSystemError}; + +pub type Result = std::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Io(#[from] std::io::Error), + + #[error(transparent)] + LairError(OneErr), + + #[error(transparent)] + ConductorError(#[from] ConductorError), + + #[error(transparent)] + SerializedBytesError(#[from] SerializedBytesError), + + #[error(transparent)] + MrBundleError(#[from] MrBundleError), + + #[error(transparent)] + FileSystemError(#[from] FileSystemError), + + #[error("JSON serialization error: {0}")] + SerdeJsonError(#[from] serde_json::Error), + + #[error("Lock error: {0}")] + LockError(String), + + #[error(transparent)] + UrlParseError(#[from] url::ParseError), + + #[error("ConductorApiError: `{0:?}`")] + ConductorApiError(ConductorApiError), + + #[error("Http server error: {0}")] + HttpServerError(String), + + #[error("Filesystem error: {0}")] + FilesystemError(String), + + #[error("Sign zome call error: {0}")] + SignZomeCallError(String), + + #[error("Admin websocket error: {0}")] + AdminWebsocketError(String), + + #[error("Error connecting websocket: {0}")] + WebsocketConnectionError(String), + + #[error("Error opening app: {0}")] + OpenAppError(String), + + #[error("App \"{0}\" does not exist ")] + AppDoesNotExist(String), + + #[error("Holochain has not been initialized yet")] + HolochainNotInitializedError, + + #[error("App \"{0}\" does not have any UI")] + AppDoesNotHaveUIError(String), + + #[error(transparent)] + UpdateAppError(#[from] UpdateAppError), +} + +impl Serialize for Error { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + serializer.serialize_str(self.to_string().as_ref()) + } +} diff --git a/crates/holochain-manager/src/features.rs b/crates/holochain-manager/src/features.rs new file mode 100644 index 00000000..37e319f8 --- /dev/null +++ b/crates/holochain-manager/src/features.rs @@ -0,0 +1,15 @@ +#[cfg(all(feature = "gossip_arc_empty", feature = "gossip_arc_full"))] +compile_error!( + "The `gossip_arc_empty` and `gossip_arc_full` features are both enabled, which is an error. Please enable only one." +); +#[cfg(all(feature = "gossip_arc_empty", feature = "gossip_arc_normal"))] +compile_error!( + "The `gossip_arc_empty` and `gossip_arc_normal` features are both enabled, which is an error. Please enable only one." +); +#[cfg(all(feature = "gossip_arc_full", feature = "gossip_arc_normal"))] +compile_error!( + "The `gossip_arc_full` and `gossip_arc_normal` features are both enabled, which is an error. Please enable only one." +); + +#[cfg(all(not(feature = "gossip_arc_empty"), not(feature = "gossip_arc_full"), not(feature = "gossip_arc_normal")))] +compile_error!("All of the `gossip_arc_empty`, `gossip_arc_full`, and `gossip_arc_normal` features are disabled. Please enable one of them."); diff --git a/crates/tauri-plugin-holochain/src/filesystem.rs b/crates/holochain-manager/src/filesystem.rs similarity index 100% rename from crates/tauri-plugin-holochain/src/filesystem.rs rename to crates/holochain-manager/src/filesystem.rs diff --git a/crates/holochain-manager/src/holochain.udl b/crates/holochain-manager/src/holochain.udl new file mode 100644 index 00000000..1178b0bc --- /dev/null +++ b/crates/holochain-manager/src/holochain.udl @@ -0,0 +1,21 @@ +namespace holochain { + HolochainRuntime launch_holochain_runtime(string passphrase, HolochainPluginConfig config); +}; + +dictionary HolochainRuntime { + FileSystem filesystem; + Arc>> apps_websockets_auths; + u16 admin_port; + ConductorHandle conductor_handle; + _signal_handle? SrvHnd; +}; + +dictionary WANNetworkConfig { + string bootstrap_url; + string signal_url; +}; + +dictionary HolochainPluginConfig { + WANNetworkConfig? wan_network_config; + string holochain_dir; +}; diff --git a/crates/holochain-manager/src/http_server.rs b/crates/holochain-manager/src/http_server.rs new file mode 100644 index 00000000..6e5b78ed --- /dev/null +++ b/crates/holochain-manager/src/http_server.rs @@ -0,0 +1,56 @@ +use crate::filesystem::FileSystem; + +pub fn pong_iframe() -> String { + format!("") +} + +pub async fn read_asset( + fs: &FileSystem, + app_id: &String, + mut asset_name: String, +) -> crate::Result, Option)>> { + log::debug!("Reading asset from filesystem. Asset name: {}", asset_name); + if asset_name.starts_with("/") { + asset_name = asset_name + .strip_prefix("/") + .expect("Failed to strip prefix") + .to_string(); + } + if asset_name == "" { + asset_name = String::from("index.html"); + } + + let assets_path = fs.bundle_store.get_ui_path(&app_id)?; + let asset_file = assets_path.join(asset_name); + + let mime_guess = mime_guess::from_path(asset_file.clone()); + + let mime_type = match mime_guess.first() { + Some(mime) => Some(mime.essence_str().to_string()), + None => { + log::warn!("Could not determine MIME Type of file '{:?}'", asset_file); + None + } + }; + + match std::fs::read(asset_file.clone()) { + Ok(asset) => Ok(Some((asset, mime_type))), + Err(_e) => { + // Fallback to "index.html" to support push-based client-side routing without hashing + let asset_file = assets_path.join(String::from("index.html")); + let mime_guess = mime_guess::from_path(asset_file.clone()); + + let mime_type = match mime_guess.first() { + Some(mime) => Some(mime.essence_str().to_string()), + None => { + log::warn!("Could not determine MIME Type of file '{:?}'", asset_file); + None + } + }; + match std::fs::read(asset_file.clone()) { + Ok(asset) => Ok(Some((asset, mime_type))), + Err(_e) => Ok(None) + } + }, + } +} diff --git a/crates/tauri-plugin-holochain/src/lair_signer.rs b/crates/holochain-manager/src/lair_signer.rs similarity index 100% rename from crates/tauri-plugin-holochain/src/lair_signer.rs rename to crates/holochain-manager/src/lair_signer.rs diff --git a/crates/tauri-plugin-holochain/src/launch.rs b/crates/holochain-manager/src/launch.rs similarity index 98% rename from crates/tauri-plugin-holochain/src/launch.rs rename to crates/holochain-manager/src/launch.rs index b5c0d78d..a0754ea3 100644 --- a/crates/tauri-plugin-holochain/src/launch.rs +++ b/crates/holochain-manager/src/launch.rs @@ -11,7 +11,7 @@ use holochain_client::AdminWebsocket; use crate::{ filesystem::FileSystem, launch::signal::{can_connect_to_signal_server, run_local_signal_service}, - HolochainPluginConfig, HolochainRuntime, + HolochainManagerConfig, HolochainRuntime, }; mod mdns; @@ -38,7 +38,7 @@ fn override_gossip_arc_clamping() -> Option { /// Launch the holochain conductor in the background pub async fn launch_holochain_runtime( passphrase: BufRead, - config: HolochainPluginConfig, + config: HolochainManagerConfig, ) -> crate::Result { // let mut lock = RUNNING_HOLOCHAIN.write().await; diff --git a/crates/tauri-plugin-holochain/src/launch/mdns.rs b/crates/holochain-manager/src/launch/mdns.rs similarity index 66% rename from crates/tauri-plugin-holochain/src/launch/mdns.rs rename to crates/holochain-manager/src/launch/mdns.rs index 5dff1fbd..eabe622e 100644 --- a/crates/tauri-plugin-holochain/src/launch/mdns.rs +++ b/crates/holochain-manager/src/launch/mdns.rs @@ -19,7 +19,8 @@ pub async fn spawn_mdns_bootstrap(admin_port: u16) -> crate::Result<()> { "Could not connect to websocket: {err:?}" )) })?; - tauri::async_runtime::spawn(async move { + + tokio::spawn(async move { let mut spaces_listened_to: HashSet = HashSet::new(); let mut cells_ids_broadcasted: HashMap< (KitsuneSpace, KitsuneAgent), @@ -29,13 +30,13 @@ pub async fn spawn_mdns_bootstrap(admin_port: u16) -> crate::Result<()> { let Ok(agent_infos) = admin_ws.agent_info(None).await else { continue; }; - + // let cell_info: Vec = let spaces: HashSet = agent_infos .iter() .map(|agent_info| agent_info.space.as_ref().clone()) .collect(); - + for space in spaces { if !spaces_listened_to.contains(&space) { if let Err(err) = spawn_listen_to_space_task(space.clone(), admin_port).await { @@ -45,7 +46,7 @@ pub async fn spawn_mdns_bootstrap(admin_port: u16) -> crate::Result<()> { spaces_listened_to.insert(space); } } - + for agent_info in agent_infos { let cell_id = ( agent_info.space.as_ref().clone(), @@ -71,7 +72,7 @@ pub async fn spawn_mdns_bootstrap(admin_port: u16) -> crate::Result<()> { // store handle in self cells_ids_broadcasted.insert(cell_id, handle); } - + async_std::task::sleep(Duration::from_secs(5)).await; } }); @@ -89,52 +90,50 @@ pub async fn spawn_listen_to_space_task(space: KitsuneSpace, admin_port: u16) -> })?; let space_b64 = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(&space[..]); - tauri::async_runtime::spawn(async move { - let stream = mdns_listen(space_b64); - tokio::pin!(stream); - while let Some(maybe_response) = stream.next().await { - match maybe_response { - Ok(response) => { - log::info!("Peer found via MDNS {:?}", response); - // Decode response - let maybe_agent_info_signed: Result = - rmp_decode(&mut &*response.buffer); - if let Err(e) = maybe_agent_info_signed { - log::error!("Failed to decode MDNS peer {:?}", e); + let stream = mdns_listen(space_b64); + tokio::pin!(stream); + while let Some(maybe_response) = stream.next().await { + match maybe_response { + Ok(response) => { + log::info!("Peer found via MDNS {:?}", response); + // Decode response + let maybe_agent_info_signed: Result = + rmp_decode(&mut &*response.buffer); + if let Err(e) = maybe_agent_info_signed { + log::error!("Failed to decode MDNS peer {:?}", e); + continue; + } + if let Ok(remote_agent_info_signed) = maybe_agent_info_signed { + // Add to local storage + let Ok(agent_infos) = admin_ws.agent_info(None).await else { continue; - } - if let Ok(remote_agent_info_signed) = maybe_agent_info_signed { - // Add to local storage - let Ok(agent_infos) = admin_ws.agent_info(None).await else { - continue; - }; + }; - if agent_infos - .iter() - .find(|agent_info| { - remote_agent_info_signed - .agent - .as_ref() - .eq(agent_info.agent.as_ref()) - }) - .is_none() + if agent_infos + .iter() + .find(|agent_info| { + remote_agent_info_signed + .agent + .as_ref() + .eq(agent_info.agent.as_ref()) + }) + .is_none() + { + log::error!("Adding agent info {remote_agent_info_signed:?}"); + if let Err(e) = admin_ws + .add_agent_info(vec![remote_agent_info_signed]) + .await { - log::error!("Adding agent info {remote_agent_info_signed:?}"); - if let Err(e) = admin_ws - .add_agent_info(vec![remote_agent_info_signed]) - .await - { - log::error!("Failed to store MDNS peer {:?}", e); - } + log::error!("Failed to store MDNS peer {:?}", e); } } } - Err(e) => { - log::error!("Failed to get peers from MDNS {:?}", e); - } + } + Err(e) => { + log::error!("Failed to get peers from MDNS {:?}", e); } } - }); + } Ok(()) } diff --git a/crates/tauri-plugin-holochain/src/launch/signal.rs b/crates/holochain-manager/src/launch/signal.rs similarity index 100% rename from crates/tauri-plugin-holochain/src/launch/signal.rs rename to crates/holochain-manager/src/launch/signal.rs diff --git a/crates/holochain-manager/src/lib.rs b/crates/holochain-manager/src/lib.rs new file mode 100644 index 00000000..34937e95 --- /dev/null +++ b/crates/holochain-manager/src/lib.rs @@ -0,0 +1,314 @@ +use std::{ + collections::HashMap, + path::PathBuf, + sync::Arc, +}; + +use async_std::sync::Mutex; +use lair_signer::LairAgentSignerWithProvenance; +use holochain::{ + conductor::ConductorHandle, + prelude::{AppBundle, MembraneProof, NetworkSeed, RoleName}, +}; +use holochain_client::{AdminWebsocket, AgentPubKey, AppInfo, AppWebsocket, InstalledAppId}; +use holochain_types::{web_app::WebAppBundle, websocket::AllowedOrigins}; +use tx5_signal_srv::SrvHnd; +use url2::Url2; + +pub mod commands; +pub mod config; +pub mod error; +mod features; +pub mod filesystem; +pub mod http_server; +pub mod lair_signer; +pub mod launch; + +use commands::{install_app, install_web_app, update_app, UpdateAppError}; +pub use error::{Error, Result}; +use filesystem::{AppBundleStore, BundleStore, FileSystem}; + + +#[derive(Clone)] +pub struct AppWebsocketAuth { + pub app_id: String, + pub main_window: bool, + pub app_websocket_port: u16, + pub token: Vec, +} + +pub struct HolochainRuntime { + pub filesystem: FileSystem, + pub apps_websockets_auths: Arc>>, + pub admin_port: u16, + pub(crate) conductor_handle: ConductorHandle, + pub(crate) _signal_handle: Option, +} + + +impl HolochainRuntime { + /// Builds an `AdminWebsocket` ready to use + pub async fn admin_websocket(&self) -> crate::Result { + let admin_ws = + AdminWebsocket::connect(format!("localhost:{}", self.admin_port)) + .await + .map_err(|err| crate::Error::WebsocketConnectionError(format!("{err:?}")))?; + Ok(admin_ws) + } + + pub async fn get_app_websocket_auth( + &self, + app_id: &InstalledAppId, + main_window: bool, + allowed_origins: AllowedOrigins, + ) -> crate::Result { + let mut apps_websockets_auths = self.apps_websockets_auths.lock().await; + let existing_auth = apps_websockets_auths + .iter() + .find(|auth| auth.main_window == main_window && auth.app_id.eq(app_id)); + if let Some(app_websocket_auth) = existing_auth { + return Ok(app_websocket_auth.clone()); + } + + let admin_ws = self.admin_websocket().await?; + + let app_port = admin_ws + .attach_app_interface(0, allowed_origins, Some(app_id.clone())) + .await + .map_err(|err| crate::Error::ConductorApiError(err))?; + + let response = admin_ws + .issue_app_auth_token( + holochain_conductor_api::IssueAppAuthenticationTokenPayload { + installed_app_id: app_id.clone(), + expiry_seconds: 999999999, + single_use: false, + }, + ) + .await + .map_err(|err| crate::Error::ConductorApiError(err))?; + + let token = response.token; + + let app_websocket_auth = AppWebsocketAuth { + app_id: app_id.clone(), + main_window, + app_websocket_port: app_port, + token, + }; + + apps_websockets_auths.push(app_websocket_auth.clone()); + Ok(app_websocket_auth) + } + + /// Builds an `AppWebsocket` for the given app ready to use + /// + /// * `app_id` - the app to build the `AppWebsocket` for + pub async fn app_websocket(&self, app_id: InstalledAppId, allowed_origins: AllowedOrigins) -> crate::Result { + let app_websocket_auth = self.get_app_websocket_auth(&app_id, false, allowed_origins).await?; + let app_ws = AppWebsocket::connect( + format!("localhost:{}", app_websocket_auth.app_websocket_port), + app_websocket_auth.token, + Arc::new(LairAgentSignerWithProvenance::new(Arc::new( + self + .conductor_handle + .keystore() + .lair_client() + .clone(), + ))), + ) + .await + .map_err(|err| crate::Error::WebsocketConnectionError(format!("{err:?}")))?; + + Ok(app_ws) + } + + /// Install the given `WebAppBundle` in the holochain runtime + /// It installs the hApp in the holochain conductor, and extracts the UI for it to be opened using `Self::web_happ_window_builder()` + /// + /// * `app_id` - the app id to give to the installed app + /// * `web_app_bundle` - the web-app bundle to install + /// * `membrane_proofs` - the input membrane proofs for the app + /// * `agent` - the agent to install the app for + /// * `network_seed` - the network seed for the app + pub async fn install_web_app( + &self, + app_id: InstalledAppId, + web_app_bundle: WebAppBundle, + membrane_proofs: HashMap, + agent: Option, + network_seed: Option, + ) -> crate::Result { + self.filesystem + .bundle_store + .store_web_happ_bundle(app_id.clone(), &web_app_bundle) + .await?; + + let admin_ws = self.admin_websocket().await?; + let app_info = install_web_app( + &admin_ws, + app_id.clone(), + web_app_bundle, + membrane_proofs, + agent, + network_seed, + ) + .await?; + + Ok(app_info) + } + + /// Install the given `AppBundle` in the holochain conductor + /// + /// * `app_id` - the app id to give to the installed app + /// * `app_bundle` - the web-app bundle to install + /// * `membrane_proofs` - the input membrane proofs for the app + /// * `agent` - the agent to install the app for + /// * `network_seed` - the network seed for the app + pub async fn install_app( + &self, + app_id: InstalledAppId, + app_bundle: AppBundle, + membrane_proofs: HashMap, + agent: Option, + network_seed: Option, + ) -> crate::Result { + let admin_ws = self.admin_websocket().await?; + + self.filesystem + .bundle_store + .store_happ_bundle(app_id.clone(), &app_bundle)?; + + let app_info = install_app( + &admin_ws, + app_id.clone(), + app_bundle, + membrane_proofs, + agent, + network_seed, + ) + .await?; + + Ok(app_info) + } + + /// Updates the coordinator zomes and UI for the given app with an updated `WebAppBundle` + /// + /// * `app_id` - the app to update + /// * `web_app_bundle` - the new version of the web-hApp bundle + pub async fn update_web_app( + &self, + app_id: InstalledAppId, + web_app_bundle: WebAppBundle, + ) -> crate::Result<()> { + self.filesystem + .bundle_store + .store_web_happ_bundle(app_id.clone(), &web_app_bundle) + .await?; + + let admin_ws = self + .admin_websocket() + .await + .map_err(|_err| UpdateAppError::WebsocketError)?; + update_app( + &admin_ws, + app_id.clone(), + web_app_bundle.happ_bundle().await?, + ) + .await?; + + Ok(()) + } + + /// Updates the coordinator zomes for the given app with an updated `AppBundle` + /// + /// * `app_id` - the app to update + /// * `app_bundle` - the new version of the hApp bundle + pub async fn update_app( + &self, + app_id: InstalledAppId, + app_bundle: AppBundle, + ) -> std::result::Result<(), UpdateAppError> { + let mut admin_ws = self + .admin_websocket() + .await + .map_err(|_err| UpdateAppError::WebsocketError)?; + let app_info = update_app(&mut admin_ws, app_id.clone(), app_bundle).await?; + + Ok(app_info) + } + + /// Checks whether it is necessary to update the hApp, and if so, + /// updates the coordinator zomes for the given app with an updated `AppBundle` + /// + /// To do the check it compares the hash of the `AppBundle` that was installed for the given `app_id` + /// with the hash of the `current_app_bundle`, and proceeds to update the coordinator zomes for the app if they are different + /// + /// * `app_id` - the app to update + /// * `current_app_bundle` - the new version of the hApp bundle + pub async fn update_app_if_necessary( + &self, + app_id: InstalledAppId, + current_app_bundle: AppBundle, + ) -> crate::Result<()> { + let hash = AppBundleStore::app_bundle_hash(¤t_app_bundle)?; + + let installed_apps = self + .filesystem + .bundle_store + .installed_apps_store + .get()?; + let Some(installed_app_info) = installed_apps.get(&app_id) else { + return Err(crate::UpdateAppError::AppNotFound(app_id))?; + }; + + if !installed_app_info.happ_bundle_hash.eq(&hash) { + self.update_app(app_id, current_app_bundle).await?; + } + + Ok(()) + } + + /// Checks whether it is necessary to update the web-hApp, and if so, + /// updates the coordinator zomes and the UI for the given app with an updated `WebAppBundle` + /// + /// To do the check it compares the hash of the `WebAppBundle` that was installed for the given `app_id` + /// with the hash of the `current_web_app_bundle`, and proceeds to update the coordinator zomes and the UI for the app if they are different + /// + /// * `app_id` - the app to update + /// * `current_web_app_bundle` - the new version of the hApp bundle + pub async fn update_web_app_if_necessary( + &self, + app_id: InstalledAppId, + current_web_app_bundle: WebAppBundle, + ) -> crate::Result<()> { + let hash = BundleStore::web_app_bundle_hash(¤t_web_app_bundle)?; + + let installed_apps = self + .filesystem + .bundle_store + .installed_apps_store + .get()?; + let Some(installed_app_info) = installed_apps.get(&app_id) else { + return Err(crate::UpdateAppError::AppNotFound(app_id))?; + }; + + if !installed_app_info.happ_bundle_hash.eq(&hash) { + self.update_web_app(app_id, current_web_app_bundle).await?; + } + + Ok(()) + } +} + +pub struct WANNetworkConfig { + pub bootstrap_url: Url2, + pub signal_url: Url2, +} + +pub struct HolochainManagerConfig { + /// If `None`, no WAN networking will take place, only mDNS based networking + /// Peers in the same LAN will still be able to communicate with each other + pub wan_network_config: Option, + pub holochain_dir: PathBuf, +} diff --git a/crates/tauri-plugin-holochain/Cargo.toml b/crates/tauri-plugin-holochain/Cargo.toml index ce86b934..9017077d 100644 --- a/crates/tauri-plugin-holochain/Cargo.toml +++ b/crates/tauri-plugin-holochain/Cargo.toml @@ -12,54 +12,16 @@ tauri = { version = "2.0.0-rc", features = [ "devtools", "native-tls-vendored", ] } - -# Holochain dependencies -mr_bundle = "0.3.2" -holochain = "0.3.2" +holochain-manager = { path = "../holochain-manager" } +holochain_client = "0.5.1" holochain_types = "0.3.2" -holochain_keystore = "0.3.2" -holochain_conductor_api = "0.3.2" - -kitsune_p2p_mdns = "0.3.2" -kitsune_p2p_types = "0.3.2" - -tx5-signal-srv = "0.0.14-alpha" -tx5-signal = "0.0.14-alpha" - -# Lair dependencies hc_seed_bundle = "0.2" -lair_keystore = "0.4.5" -lair_keystore_api = "0.4.5" -# Holochain client -holochain_client = "0.5.1" - -local-ip-address = "0.6" -serde_json = "1" -sha256 = "1" -log = "0.4" -symlink = "0.1.0" -nanoid = "0.4.0" -async-std = "1.12" serde = "1.0.193" +one_err = "0" thiserror = "1.0" url = "2.4.0" -url2 = "0.0.6" -zip = { version = "0.6" } -bzip2 = { version = "0.4", features = ["static"] } -portpicker = "0.1" -mime_guess = "2.0.4" -hyper = { version = "1", features = ["full"] } -hyper-util = { version = "0.1", features = ["full"] } -http-body-util = "0.1" -tls-listener = "0.8" -futures = "0.3" -either = "*" -tokio = "1" -one_err = "0" -base64 = "0.22" -anyhow = "1" -async-trait = "0.1" +log = "0.4" [build-dependencies] tauri-plugin = { version = "2.0.0-rc", features = ["build"] } diff --git a/crates/tauri-plugin-holochain/build.rs b/crates/tauri-plugin-holochain/build.rs index 4405e9da..83303b81 100644 --- a/crates/tauri-plugin-holochain/build.rs +++ b/crates/tauri-plugin-holochain/build.rs @@ -8,7 +8,5 @@ const COMMANDS: &[&str] = &[ fn main() { tauri_plugin::Builder::new(COMMANDS) - // .android_path("android") - // .ios_path("ios") .build(); } diff --git a/crates/tauri-plugin-holochain/src/commands.rs b/crates/tauri-plugin-holochain/src/commands.rs index 23bd0729..486bfece 100644 --- a/crates/tauri-plugin-holochain/src/commands.rs +++ b/crates/tauri-plugin-holochain/src/commands.rs @@ -2,4 +2,4 @@ pub mod get_runtime_info; pub mod install_web_app; pub mod list_apps; pub mod open_app; -pub mod sign_zome_call; +pub mod sign_zome_call; \ No newline at end of file diff --git a/crates/tauri-plugin-holochain/src/commands/install_web_app.rs b/crates/tauri-plugin-holochain/src/commands/install_web_app.rs index 6e89ee4d..29c90e24 100644 --- a/crates/tauri-plugin-holochain/src/commands/install_web_app.rs +++ b/crates/tauri-plugin-holochain/src/commands/install_web_app.rs @@ -1,273 +1,8 @@ -use std::{ - collections::{BTreeMap, HashMap}, - path::PathBuf, -}; - -use holochain::prelude::{ - AppBundle, AppBundleError, AppBundleSource, AppManifest, CoordinatorBundle, - CoordinatorManifest, DnaBundle, DnaError, DnaFile, DnaHash, MembraneProof, NetworkSeed, - RoleName, UpdateCoordinatorsPayload, ZomeDependency, ZomeError, ZomeLocation, ZomeManifest, -}; -use holochain_client::{ - AdminWebsocket, AgentPubKey, AppInfo, ConductorApiError, InstallAppPayload, InstalledAppId, -}; -use holochain_conductor_api::{AppInfoStatus, CellInfo}; -use holochain_types::web_app::WebAppBundle; -use mr_bundle::{error::MrBundleError, Bundle, ResourceBytes}; - -use crate::filesystem::FileSystemError; - -pub async fn install_web_app( - admin_ws: &AdminWebsocket, - app_id: String, - bundle: WebAppBundle, - membrane_proofs: HashMap, - agent: Option, - network_seed: Option, -) -> crate::Result { - let app_info = install_app( - admin_ws, - app_id.clone(), - bundle.happ_bundle().await?, - membrane_proofs, - agent, - network_seed, - ) - .await?; - - log::info!("Installed web-app's ui {app_id:?}"); - - Ok(app_info) -} - -pub async fn install_app( - admin_ws: &AdminWebsocket, - app_id: String, - bundle: AppBundle, - membrane_proofs: HashMap, - agent: Option, - network_seed: Option, -) -> crate::Result { - log::info!("Installing app {}", app_id); - - let agent_key = match agent { - Some(agent) => agent, - None => admin_ws - .generate_agent_pub_key() - .await - .map_err(|err| crate::Error::ConductorApiError(err))?, - }; - - let app_info = admin_ws - .install_app(InstallAppPayload { - agent_key, - membrane_proofs, - network_seed, - source: AppBundleSource::Bundle(bundle), - installed_app_id: Some(app_id.clone()), - }) - .await - .map_err(|err| crate::Error::ConductorApiError(err))?; - log::info!("Installed app {app_info:?}"); - - let response = admin_ws - .enable_app(app_id.clone()) - .await - .map_err(|err| crate::Error::ConductorApiError(err))?; - - log::info!("Enabled app {app_id:?}"); - - Ok(response.app) -} - #[derive(Debug, thiserror::Error)] pub enum UpdateAppError { - #[error(transparent)] - AppBundleError(#[from] AppBundleError), - - #[error(transparent)] - ZomeError(#[from] ZomeError), - - #[error(transparent)] - TauriError(#[from] tauri::Error), - - #[error(transparent)] - MrBundleError(#[from] MrBundleError), - - #[error(transparent)] - FileSystemError(#[from] FileSystemError), - - #[error(transparent)] - DnaError(#[from] DnaError), - - #[error("ConductorApiError: `{0:?}`")] - ConductorApiError(ConductorApiError), - - #[error("Error connecting to the websocket")] - WebsocketError, - - #[error("The given app was not found: {0}")] - AppNotFound(String), - - #[error("The role {0} was not found the app {1}")] - RoleNotFound(RoleName, InstalledAppId), -} - -pub async fn update_app( - admin_ws: &AdminWebsocket, - app_id: String, - bundle: AppBundle, -) -> Result<(), UpdateAppError> { - log::info!( - "Checking whether the coordinator zomes for app {} need to be updated", - app_id - ); - - // Get the DNA def from the admin websocket - let apps = admin_ws - .list_apps(None) - .await - .map_err(|err| UpdateAppError::ConductorApiError(err))?; - - let mut app = apps - .into_iter() - .find(|app| app.installed_app_id.eq(&app_id)) - .ok_or(UpdateAppError::AppNotFound(app_id.clone()))?; - - let new_dna_files = resolve_dna_files(bundle).await?; - - let mut updated = false; - - for (role_name, new_dna_file) in new_dna_files { - let cells = app - .cell_info - .remove(&role_name) - .ok_or(UpdateAppError::RoleNotFound( - role_name.clone(), - app.installed_app_id.clone(), - ))?; - - for cell in cells { - let mut zomes: Vec = Vec::new(); - let mut resources: Vec<(PathBuf, ResourceBytes)> = Vec::new(); - - let dna_hash = match cell { - CellInfo::Provisioned(c) => c.cell_id.dna_hash().clone(), - CellInfo::Cloned(c) => c.cell_id.dna_hash().clone(), - CellInfo::Stem(c) => c.original_dna_hash.clone(), - }; - let old_dna_def = admin_ws - .get_dna_definition(dna_hash.clone()) - .await - .map_err(|err| UpdateAppError::ConductorApiError(err))?; - - for (zome_name, coordinator_zome) in new_dna_file.dna_def().coordinator_zomes.iter() { - let deps = coordinator_zome - .clone() - .erase_type() - .dependencies() - .to_vec(); - let dependencies = deps - .into_iter() - .map(|name| ZomeDependency { name }) - .collect(); - - if let Some(old_zome_def) = old_dna_def - .coordinator_zomes - .iter() - .find(|(zome, _)| zome.eq(&zome_name)) - { - if !old_zome_def - .1 - .wasm_hash(&zome_name)? - .eq(&coordinator_zome.wasm_hash(&zome_name)?) - { - log::info!("Updating coordinator zome {zome_name} for role {role_name}"); - let resource_path = PathBuf::from(zome_name.0.to_string()); - zomes.push(ZomeManifest { - name: zome_name.clone(), - hash: None, - dylib: None, - location: ZomeLocation::Bundled(resource_path.clone()), - dependencies: Some(dependencies), - }); - let wasm = new_dna_file.get_wasm_for_zome(&zome_name)?; - resources.push((resource_path, wasm.code().to_vec().into())); - } - } else { - log::info!("Adding new coordinator zome {zome_name} for role {role_name}"); - let resource_path = PathBuf::from(zome_name.0.to_string()); - zomes.push(ZomeManifest { - name: zome_name.clone(), - hash: None, - dylib: None, - location: ZomeLocation::Bundled(resource_path.clone()), - dependencies: Some(dependencies), - }); - let wasm = new_dna_file.get_wasm_for_zome(&zome_name)?; - resources.push((resource_path, wasm.code().to_vec().into())); - } - } - - if !zomes.is_empty() { - let source: CoordinatorBundle = - Bundle::new(CoordinatorManifest { zomes }, resources, PathBuf::from("/"))? - .into(); - let req = UpdateCoordinatorsPayload { - dna_hash, - source: holochain_types::prelude::CoordinatorSource::Bundle(Box::new(source)), - }; - - admin_ws - .update_coordinators(req) - .await - .map_err(|err| UpdateAppError::ConductorApiError(err))?; - updated = true; - } - } - } - - if updated { - if let AppInfoStatus::Running = app.status { - admin_ws - .disable_app(app_id.clone()) - .await - .map_err(|err| UpdateAppError::ConductorApiError(err))?; - admin_ws - .enable_app(app_id.clone()) - .await - .map_err(|err| UpdateAppError::ConductorApiError(err))?; - } - log::info!("Updated app {app_id:?}"); - } - - Ok(()) -} - -async fn resolve_dna_files( - app_bundle: AppBundle, -) -> Result, UpdateAppError> { - let mut dna_files: BTreeMap = BTreeMap::new(); - - let bundle = app_bundle.into_inner(); - - for app_role in bundle.manifest().app_roles() { - if let Some(location) = app_role.dna.location { - let (dna_def, _) = resolve_location(&bundle, &location).await?; - - dna_files.insert(app_role.name.clone(), dna_def); - } - } - - Ok(dna_files) -} - -async fn resolve_location( - app_bundle: &Bundle, - location: &mr_bundle::Location, -) -> Result<(DnaFile, DnaHash), UpdateAppError> { - let bytes = app_bundle.resolve(location).await?; - let dna_bundle: DnaBundle = mr_bundle::Bundle::decode(&bytes)?.into(); - let (dna_file, original_hash) = dna_bundle.into_dna_file(Default::default()).await?; - Ok((dna_file, original_hash)) + #[error(transparent)] + UpdateAppError(#[from] holochain_manager::commands::UpdateAppError), + + #[error(transparent)] + TauriError(#[from] tauri::Error), } diff --git a/crates/tauri-plugin-holochain/src/commands/list_apps.rs b/crates/tauri-plugin-holochain/src/commands/list_apps.rs index 1c7a9daa..ed8ab4c8 100644 --- a/crates/tauri-plugin-holochain/src/commands/list_apps.rs +++ b/crates/tauri-plugin-holochain/src/commands/list_apps.rs @@ -4,12 +4,5 @@ use tauri::{command, AppHandle, Runtime}; #[command] pub(crate) async fn list_apps(app: AppHandle) -> crate::Result> { - let admin_ws = app.holochain()?.admin_websocket().await?; - - let apps = admin_ws - .list_apps(None) - .await - .map_err(|err| crate::Error::ConductorApiError(err))?; - - Ok(apps) + Ok(holochain_manager::commands::list_apps(app.holochain()?.holochain_runtime.clone()).await?) } diff --git a/crates/tauri-plugin-holochain/src/commands/sign_zome_call.rs b/crates/tauri-plugin-holochain/src/commands/sign_zome_call.rs index f85c6b15..475cd98e 100644 --- a/crates/tauri-plugin-holochain/src/commands/sign_zome_call.rs +++ b/crates/tauri-plugin-holochain/src/commands/sign_zome_call.rs @@ -1,13 +1,6 @@ -use holochain::{ - conductor::api::ZomeCall, - prelude::{CapSecret, CellId, ExternIO, FunctionName, Timestamp, ZomeCallUnsigned, ZomeName}, -}; -use holochain_client::AgentPubKey; -use holochain_types::prelude::Signature; -use lair_keystore_api::LairClient; -use serde::Deserialize; +use holochain_client::ZomeCall; use tauri::{command, AppHandle, Runtime}; - +use holochain_manager::commands::ZomeCallUnsignedTauri; use crate::HolochainExt; #[command] @@ -15,99 +8,8 @@ pub(crate) async fn sign_zome_call( app_handle: AppHandle, zome_call_unsigned: ZomeCallUnsignedTauri, ) -> crate::Result { - let zome_call_unsigned_converted: ZomeCallUnsigned = zome_call_unsigned.into(); - - let signed_zome_call = sign_zome_call_with_client( - zome_call_unsigned_converted, - &app_handle - .holochain()? - .holochain_runtime - .conductor_handle - .keystore() - .lair_client() - .clone(), - ) - .await?; - - Ok(signed_zome_call) -} - -/// Signs an unsigned zome call with the given LairClient -pub async fn sign_zome_call_with_client( - zome_call_unsigned: ZomeCallUnsigned, - client: &LairClient, -) -> crate::Result { - // sign the zome call - let pub_key = zome_call_unsigned.provenance.clone(); - let mut pub_key_2 = [0; 32]; - pub_key_2.copy_from_slice(pub_key.get_raw_32()); - - let data_to_sign = zome_call_unsigned.data_to_sign()?; - - let sig = client - .sign_by_pub_key(pub_key_2.into(), None, data_to_sign) - .await - .map_err(|err| crate::Error::LairError(err))?; - - let signature = Signature(*sig.0); - - let signed_zome_call = ZomeCall { - cell_id: zome_call_unsigned.cell_id, - zome_name: zome_call_unsigned.zome_name, - fn_name: zome_call_unsigned.fn_name, - payload: zome_call_unsigned.payload, - cap_secret: zome_call_unsigned.cap_secret, - provenance: zome_call_unsigned.provenance, - nonce: zome_call_unsigned.nonce, - expires_at: zome_call_unsigned.expires_at, - signature, - }; - - return Ok(signed_zome_call); -} - -/// The version of an unsigned zome call that's compatible with the serialization -/// behavior of tauri's IPC channel (serde serialization) -/// nonce is a byte array [u8, 32] because holochain's nonce type seems to -/// have "non-serde" deserialization behavior. -#[derive(Deserialize, Debug)] -pub struct ZomeCallUnsignedTauri { - pub provenance: AgentPubKey, - pub cell_id: CellId, - pub zome_name: ZomeName, - pub fn_name: FunctionName, - pub cap_secret: Option, - pub payload: ExternIO, - pub nonce: [u8; 32], - pub expires_at: Timestamp, -} - -impl Into for ZomeCallUnsignedTauri { - fn into(self) -> ZomeCallUnsigned { - ZomeCallUnsigned { - provenance: self.provenance, - cell_id: self.cell_id, - zome_name: self.zome_name, - fn_name: self.fn_name, - cap_secret: self.cap_secret, - payload: self.payload, - nonce: self.nonce.into(), - expires_at: self.expires_at, - } - } -} - -impl Clone for ZomeCallUnsignedTauri { - fn clone(&self) -> Self { - ZomeCallUnsignedTauri { - provenance: self.provenance.clone(), - cell_id: self.cell_id.clone(), - zome_name: self.zome_name.clone(), - fn_name: self.fn_name.clone(), - cap_secret: self.cap_secret.clone(), - payload: self.payload.clone(), - nonce: self.nonce.clone(), - expires_at: self.expires_at.clone(), - } - } + Ok(holochain_manager::commands::sign_zome_call( + app_handle.holochain()?.holochain_runtime.clone(), + zome_call_unsigned + ).await?) } diff --git a/crates/tauri-plugin-holochain/src/error.rs b/crates/tauri-plugin-holochain/src/error.rs index 0b17ef1b..bb342ceb 100644 --- a/crates/tauri-plugin-holochain/src/error.rs +++ b/crates/tauri-plugin-holochain/src/error.rs @@ -1,52 +1,21 @@ -use holochain::{conductor::error::ConductorError, prelude::SerializedBytesError}; -use holochain_client::ConductorApiError; -use mr_bundle::error::MrBundleError; -use one_err::OneErr; +use holochain_manager::commands::UpdateAppError; use serde::{ser::Serializer, Serialize}; -use crate::{commands::install_web_app::UpdateAppError, filesystem::FileSystemError}; - pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] - Io(#[from] std::io::Error), - - #[cfg(mobile)] - #[error(transparent)] - PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), - - #[error(transparent)] - LairError(OneErr), - - #[error(transparent)] - ConductorError(#[from] ConductorError), - - #[error(transparent)] - SerializedBytesError(#[from] SerializedBytesError), - - #[error(transparent)] - MrBundleError(#[from] MrBundleError), + HolochainManagerError(#[from] holochain_manager::Error), #[error(transparent)] TauriError(#[from] tauri::Error), - - #[error(transparent)] - FileSystemError(#[from] FileSystemError), - - #[error("JSON serialization error: {0}")] - SerdeJsonError(#[from] serde_json::Error), - #[error("Lock error: {0}")] LockError(String), #[error(transparent)] UrlParseError(#[from] url::ParseError), - #[error("ConductorApiError: `{0:?}`")] - ConductorApiError(ConductorApiError), - #[error("Http server error: {0}")] HttpServerError(String), @@ -73,7 +42,7 @@ pub enum Error { #[error("App \"{0}\" does not have any UI")] AppDoesNotHaveUIError(String), - + #[error(transparent)] UpdateAppError(#[from] UpdateAppError), } diff --git a/crates/tauri-plugin-holochain/src/http_server.rs b/crates/tauri-plugin-holochain/src/http_server.rs deleted file mode 100644 index a2f4bd55..00000000 --- a/crates/tauri-plugin-holochain/src/http_server.rs +++ /dev/null @@ -1,200 +0,0 @@ -// use std::net::SocketAddr; - -// use http_body_util::Full; -// use hyper::body::Bytes; -// use hyper::server::conn::http1; -// use hyper::service::service_fn; -// use hyper::{Response, StatusCode}; -// use hyper_util::rt::TokioIo; -// use tauri::{AppHandle, Runtime}; -// use tokio::net::TcpListener; - -use crate::filesystem::FileSystem; - -pub fn pong_iframe() -> String { - format!("") -} - -// pub fn window_html() -> String { -// include_str!("../../packages/frame/dist/index.html").into() -// } - -// pub async fn start_http_server( -// app_handle: AppHandle, -// ui_server_port: u16, -// ) -> crate::Result<()> { -// let addr: SocketAddr = ([127, 0, 0, 1], ui_server_port).into(); -// let listener = TcpListener::bind(addr).await?; -// tauri::async_runtime::spawn(async move { -// let app_handle = app_handle.clone(); -// // The closure inside `make_service_fn` is run for each connection, -// // creating a 'service' to handle requests for that specific connection. -// // let make_service = make_service_fn(move |_| async { -// // // While the state was moved into the make_service closure, -// // // we need to clone it here because this closure is called -// // // once for every connection. -// // // -// // // Each connection could send multiple requests, so -// // // the `Service` needs a clone to handle later requests. -// // // This is the `Service` that will handle the connection. -// // // `service_fn` is a helper to convert a function that -// // // returns a Response into a `Service`. -// // // let app_handle = app_handle.clone(); -// // Ok::<_, hyper::Error>(service_fn(move |request| { -// // let app_handle = app_handle.clone(); -// // async move {} -// // })) -// // }); - -// loop { -// let app_handle = app_handle.clone(); -// if let Ok((stream, _)) = listener.accept().await { -// let io = TokioIo::new(stream); - -// // This is the `Service` that will handle the connection. -// // `service_fn` is a helper to convert a function that -// // returns a Response into a `Service`. -// let service = service_fn(move |request| { -// let app_handle = app_handle.clone(); -// async move { -// let host = request -// .headers() -// .get("host") -// .expect("URI has no host") -// // .ok_or(crate::Error::HttpServerError(String::from( -// // "URI has no host", -// // )))? -// .clone() -// .to_str() -// .expect("Could not convert str to string") -// // .map_err(|err| crate::Error::HttpServerError(format!("{:?}", err)))? -// .to_string(); - -// if host.starts_with("ping.localhost") { -// return Response::builder() -// .status(202) -// .header("content-type", "text/html") -// .body(pong_iframe().into()); -// } -// if host.starts_with("localhost") { -// return Response::builder() -// .status(202) -// .header("content-type", "text/html") -// .body(window_html().into()); -// } - -// let split_host: Vec = -// host.split(".").into_iter().map(|s| s.to_string()).collect(); -// let lowercase_app_id = split_host.get(0).expect("Failed to get the app id"); - -// let file_name = request.uri().path(); - -// let Ok(holochain) = app_handle.holochain() else { -// return Response::builder() -// .status(StatusCode::INTERNAL_SERVER_ERROR) -// .body( -// format!("Called http UI before initializing holochain") -// .as_bytes() -// .to_vec() -// .into(), -// ); -// }; - -// let r: hyper::http::Result>> = match read_asset( -// &holochain.holochain_runtime.filesystem, -// &lowercase_app_id, -// file_name.to_string(), -// ) -// .await -// { -// Ok(Some((asset, mime_type))) => { -// let mut response_builder = Response::builder().status(202); -// if let Some(mime_type) = mime_type { -// response_builder = -// response_builder.header("content-type", mime_type); -// } - -// response_builder.body(asset.into()) -// } -// Ok(None) => Response::builder().status(404).body(vec![].into()), -// Err(e) => Ok(Response::builder() -// .status(500) -// .body(format!("{:?}", e).into()) -// .expect("Failed to build body of error response")), -// }; -// // admin_ws.close(); -// r -// } -// // Get the current count, and also increment by 1, in a single -// // // atomic operation. -// // let count = counter.fetch_add(1, Ordering::AcqRel); -// // async move { -// // Ok::<_, Error>(Response::new(Full::new(Bytes::from(format!( -// // "Request #{}", -// // count -// // ))))) -// // } -// }); - -// if let Err(err) = http1::Builder::new().serve_connection(io, service).await { -// println!("Error serving connection: {:?}", err); -// } -// } else { -// log::error!("Could not accept incoming tcp stream"); -// } -// } -// }); - -// Ok(()) -// } - -pub async fn read_asset( - fs: &FileSystem, - app_id: &String, - mut asset_name: String, -) -> crate::Result, Option)>> { - log::debug!("Reading asset from filesystem. Asset name: {}", asset_name); - if asset_name.starts_with("/") { - asset_name = asset_name - .strip_prefix("/") - .expect("Failed to strip prefix") - .to_string(); - } - if asset_name == "" { - asset_name = String::from("index.html"); - } - - let assets_path = fs.bundle_store.get_ui_path(&app_id)?; - let asset_file = assets_path.join(asset_name); - - let mime_guess = mime_guess::from_path(asset_file.clone()); - - let mime_type = match mime_guess.first() { - Some(mime) => Some(mime.essence_str().to_string()), - None => { - log::warn!("Could not determine MIME Type of file '{:?}'", asset_file); - None - } - }; - - match std::fs::read(asset_file.clone()) { - Ok(asset) => Ok(Some((asset, mime_type))), - Err(_e) => { - // Fallback to "index.html" to support push-based client-side routing without hashing - let asset_file = assets_path.join(String::from("index.html")); - let mime_guess = mime_guess::from_path(asset_file.clone()); - - let mime_type = match mime_guess.first() { - Some(mime) => Some(mime.essence_str().to_string()), - None => { - log::warn!("Could not determine MIME Type of file '{:?}'", asset_file); - None - } - }; - match std::fs::read(asset_file.clone()) { - Ok(asset) => Ok(Some((asset, mime_type))), - Err(_e) => Ok(None) - } - }, - } -} diff --git a/crates/tauri-plugin-holochain/src/lib.rs b/crates/tauri-plugin-holochain/src/lib.rs index 41ef11e9..659b55d9 100644 --- a/crates/tauri-plugin-holochain/src/lib.rs +++ b/crates/tauri-plugin-holochain/src/lib.rs @@ -1,66 +1,36 @@ use std::{ collections::{HashMap, HashSet}, - path::PathBuf, - sync::Arc, + path::PathBuf, sync::Arc, }; -use async_std::sync::Mutex; use hc_seed_bundle::dependencies::sodoken::BufRead; -use http_server::{pong_iframe, read_asset}; -use lair_signer::LairAgentSignerWithProvenance; -use launch::launch_holochain_runtime; +use holochain_manager::{http_server::{pong_iframe, read_asset}, launch::launch_holochain_runtime, AppWebsocketAuth, HolochainManagerConfig}; use tauri::{ http::response, ipc::CapabilityBuilder, plugin::{Builder, TauriPlugin}, AppHandle, Emitter, Manager, Runtime, WebviewUrl, WebviewWindowBuilder, }; +use crate::commands::install_web_app::UpdateAppError; -use holochain::{ - conductor::ConductorHandle, - prelude::{AppBundle, MembraneProof, NetworkSeed, RoleName}, -}; -use holochain_client::{AdminWebsocket, AgentPubKey, AppInfo, AppWebsocket, InstalledAppId}; -use holochain_types::{web_app::WebAppBundle, websocket::AllowedOrigins}; -use tx5_signal_srv::SrvHnd; -use url2::Url2; +use holochain_manager::HolochainRuntime; +use holochain_client::{AgentPubKey, AppInfo, InstalledAppId}; +use holochain_types::{app::AppBundle, prelude::{MembraneProof, NetworkSeed, RoleName}, web_app::WebAppBundle, websocket::AllowedOrigins}; mod commands; -mod config; mod error; mod features; -mod filesystem; -mod http_server; -mod lair_signer; -mod launch; -use commands::install_web_app::{install_app, install_web_app, update_app, UpdateAppError}; pub use error::{Error, Result}; -use filesystem::{AppBundleStore, BundleStore, FileSystem}; const ZOME_CALL_SIGNER_INITIALIZATION_SCRIPT: &'static str = include_str!("../zome-call-signer.js"); /// Access to the holochain APIs. pub struct HolochainPlugin { pub app_handle: AppHandle, - pub holochain_runtime: HolochainRuntime, + pub holochain_runtime: Arc, } -#[derive(Clone)] -pub struct AppWebsocketAuth { - pub app_id: String, - pub main_window: bool, - pub app_websocket_port: u16, - pub token: Vec, -} - -pub struct HolochainRuntime { - pub filesystem: FileSystem, - pub apps_websockets_auths: Arc>>, - pub admin_port: u16, - pub(crate) conductor_handle: ConductorHandle, - pub(crate) _signal_handle: Option, -} fn happ_origin(app_id: &String) -> String { if cfg!(target_os = "windows") { @@ -208,30 +178,11 @@ impl HolochainPlugin { Ok(window_builder) } - /// Builds an `AdminWebsocket` ready to use - pub async fn admin_websocket(&self) -> crate::Result { - let admin_ws = - AdminWebsocket::connect(format!("localhost:{}", self.holochain_runtime.admin_port)) - .await - .map_err(|err| crate::Error::WebsocketConnectionError(format!("{err:?}")))?; - Ok(admin_ws) - } - async fn get_app_websocket_auth( &self, app_id: &InstalledAppId, main_window: bool, ) -> crate::Result { - let mut apps_websockets_auths = self.holochain_runtime.apps_websockets_auths.lock().await; - let existing_auth = apps_websockets_auths - .iter() - .find(|auth| auth.main_window == main_window && auth.app_id.eq(app_id)); - if let Some(app_websocket_auth) = existing_auth { - return Ok(app_websocket_auth.clone()); - } - - let admin_ws = self.admin_websocket().await?; - // Allow any when the app is build in debug mode to allow normal tauri development pointing to http://localhost:1420 let allowed_origins = if tauri::is_dev() { AllowedOrigins::Any @@ -247,55 +198,7 @@ impl HolochainPlugin { AllowedOrigins::Origins(origins) }; - let app_port = admin_ws - .attach_app_interface(0, allowed_origins, Some(app_id.clone())) - .await - .map_err(|err| crate::Error::ConductorApiError(err))?; - - let response = admin_ws - .issue_app_auth_token( - holochain_conductor_api::IssueAppAuthenticationTokenPayload { - installed_app_id: app_id.clone(), - expiry_seconds: 999999999, - single_use: false, - }, - ) - .await - .map_err(|err| crate::Error::ConductorApiError(err))?; - - let token = response.token; - - let app_websocket_auth = AppWebsocketAuth { - app_id: app_id.clone(), - main_window, - app_websocket_port: app_port, - token, - }; - - apps_websockets_auths.push(app_websocket_auth.clone()); - Ok(app_websocket_auth) - } - - /// Builds an `AppWebsocket` for the given app ready to use - /// - /// * `app_id` - the app to build the `AppWebsocket` for - pub async fn app_websocket(&self, app_id: InstalledAppId) -> crate::Result { - let app_websocket_auth = self.get_app_websocket_auth(&app_id, false).await?; - let app_ws = AppWebsocket::connect( - format!("localhost:{}", app_websocket_auth.app_websocket_port), - app_websocket_auth.token, - Arc::new(LairAgentSignerWithProvenance::new(Arc::new( - self.holochain_runtime - .conductor_handle - .keystore() - .lair_client() - .clone(), - ))), - ) - .await - .map_err(|err| crate::Error::WebsocketConnectionError(format!("{err:?}")))?; - - Ok(app_ws) + Ok(self.holochain_runtime.get_app_websocket_auth(app_id,main_window, allowed_origins).await?) } /// Install the given `WebAppBundle` in the holochain runtime @@ -314,22 +217,14 @@ impl HolochainPlugin { agent: Option, network_seed: Option, ) -> crate::Result { - self.holochain_runtime - .filesystem - .bundle_store - .store_web_happ_bundle(app_id.clone(), &web_app_bundle) - .await?; - - let admin_ws = self.admin_websocket().await?; - let app_info = install_web_app( - &admin_ws, - app_id.clone(), - web_app_bundle, - membrane_proofs, - agent, - network_seed, - ) - .await?; + let app_info = self.holochain_runtime + .install_web_app( + app_id.clone(), + web_app_bundle, + membrane_proofs, + agent, + network_seed, + ).await?; self.app_handle.emit("holochain://app-installed", app_id)?; @@ -351,23 +246,13 @@ impl HolochainPlugin { agent: Option, network_seed: Option, ) -> crate::Result { - let admin_ws = self.admin_websocket().await?; - - self.holochain_runtime - .filesystem - .bundle_store - .store_happ_bundle(app_id.clone(), &app_bundle)?; - - let app_info = install_app( - &admin_ws, + let app_info = self.holochain_runtime.install_app( app_id.clone(), app_bundle, membrane_proofs, agent, network_seed, - ) - .await?; - + ).await?; self.app_handle.emit("holochain://app-installed", app_id)?; Ok(app_info) } @@ -382,22 +267,9 @@ impl HolochainPlugin { web_app_bundle: WebAppBundle, ) -> crate::Result<()> { self.holochain_runtime - .filesystem - .bundle_store - .store_web_happ_bundle(app_id.clone(), &web_app_bundle) + .update_web_app(app_id.clone(), web_app_bundle) .await?; - let admin_ws = self - .admin_websocket() - .await - .map_err(|_err| UpdateAppError::WebsocketError)?; - update_app( - &admin_ws, - app_id.clone(), - web_app_bundle.happ_bundle().await?, - ) - .await?; - self.app_handle.emit("holochain://app-updated", app_id)?; Ok(()) @@ -412,79 +284,12 @@ impl HolochainPlugin { app_id: InstalledAppId, app_bundle: AppBundle, ) -> std::result::Result<(), UpdateAppError> { - let mut admin_ws = self - .admin_websocket() - .await - .map_err(|_err| UpdateAppError::WebsocketError)?; - let app_info = update_app(&mut admin_ws, app_id.clone(), app_bundle).await?; - + let app_info = self.holochain_runtime.update_app(app_id.clone(), app_bundle).await?; + self.app_handle.emit("holochain://app-updated", app_id)?; + Ok(app_info) } - - /// Checks whether it is necessary to update the hApp, and if so, - /// updates the coordinator zomes for the given app with an updated `AppBundle` - /// - /// To do the check it compares the hash of the `AppBundle` that was installed for the given `app_id` - /// with the hash of the `current_app_bundle`, and proceeds to update the coordinator zomes for the app if they are different - /// - /// * `app_id` - the app to update - /// * `current_app_bundle` - the new version of the hApp bundle - pub async fn update_app_if_necessary( - &self, - app_id: InstalledAppId, - current_app_bundle: AppBundle, - ) -> crate::Result<()> { - let hash = AppBundleStore::app_bundle_hash(¤t_app_bundle)?; - - let installed_apps = self - .holochain_runtime - .filesystem - .bundle_store - .installed_apps_store - .get()?; - let Some(installed_app_info) = installed_apps.get(&app_id) else { - return Err(crate::UpdateAppError::AppNotFound(app_id))?; - }; - - if !installed_app_info.happ_bundle_hash.eq(&hash) { - self.update_app(app_id, current_app_bundle).await?; - } - - Ok(()) - } - - /// Checks whether it is necessary to update the web-hApp, and if so, - /// updates the coordinator zomes and the UI for the given app with an updated `WebAppBundle` - /// - /// To do the check it compares the hash of the `WebAppBundle` that was installed for the given `app_id` - /// with the hash of the `current_web_app_bundle`, and proceeds to update the coordinator zomes and the UI for the app if they are different - /// - /// * `app_id` - the app to update - /// * `current_web_app_bundle` - the new version of the hApp bundle - pub async fn update_web_app_if_necessary( - &self, - app_id: InstalledAppId, - current_web_app_bundle: WebAppBundle, - ) -> crate::Result<()> { - let hash = BundleStore::web_app_bundle_hash(¤t_web_app_bundle)?; - - let installed_apps = self - .holochain_runtime - .filesystem - .bundle_store - .installed_apps_store - .get()?; - let Some(installed_app_info) = installed_apps.get(&app_id) else { - return Err(crate::UpdateAppError::AppNotFound(app_id))?; - }; - - if !installed_app_info.happ_bundle_hash.eq(&hash) { - self.update_web_app(app_id, current_web_app_bundle).await?; - } - - Ok(()) - } } // Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the holochain APIs. @@ -503,18 +308,6 @@ impl> crate::HolochainExt for T { } } -pub struct WANNetworkConfig { - pub bootstrap_url: Url2, - pub signal_url: Url2, -} - -pub struct HolochainPluginConfig { - /// If `None`, no WAN networking will take place, only mDNS based networking - /// Peers in the same LAN will still be able to communicate with each other - pub wan_network_config: Option, - pub holochain_dir: PathBuf, -} - fn plugin_builder() -> Builder { Builder::new("holochain") .invoke_handler(tauri::generate_handler![ @@ -534,9 +327,6 @@ fn plugin_builder() -> Builder { } // prepare our response tauri::async_runtime::block_on(async move { - // let mutex = app_handle.state::>(); - // let mut admin_ws = mutex.lock().await; - let uri_without_protocol = request .uri() .to_string() @@ -617,7 +407,7 @@ fn plugin_builder() -> Builder { } /// Initializes the plugin, waiting for holochain to launch before finishing the app's setup. -pub fn init(passphrase: BufRead, config: HolochainPluginConfig) -> TauriPlugin { +pub fn init(passphrase: BufRead, config: HolochainManagerConfig) -> TauriPlugin { plugin_builder() .setup(|app, _api| { let handle = app.clone(); @@ -634,7 +424,7 @@ pub fn init(passphrase: BufRead, config: HolochainPluginConfig) -> T /// If you use this version of init, you should listen to the `holochain://setup-completed` event in your `setup()` hook pub fn async_init( passphrase: BufRead, - config: HolochainPluginConfig, + config: HolochainManagerConfig, ) -> TauriPlugin { plugin_builder() .setup(|app, _api| { @@ -658,7 +448,7 @@ pub fn async_init( async fn launch_and_setup_holochain( app_handle: AppHandle, passphrase: BufRead, - config: HolochainPluginConfig, + config: HolochainManagerConfig, ) -> crate::Result<()> { // let http_server_port = portpicker::pick_unused_port().expect("No ports free"); // http_server::start_http_server(app_handle.clone(), http_server_port).await?; @@ -668,7 +458,7 @@ async fn launch_and_setup_holochain( let p = HolochainPlugin:: { app_handle: app_handle.clone(), - holochain_runtime, + holochain_runtime: Arc::new(holochain_runtime), }; // manage state so it is accessible by the commands