diff --git a/Cargo.lock b/Cargo.lock index f56059de..cc02660e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "article-core" version = "0.1.0" -source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.0.2#5a9e2af2433a04129f2ad0c11acafb187c66f649" +source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.1.0#4b89097d8791a7190d01de1c576979c93df0013d" dependencies = [ "blake3", "image", @@ -373,7 +373,7 @@ dependencies = [ [[package]] name = "article-makepad" version = "0.1.0" -source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.0.2#5a9e2af2433a04129f2ad0c11acafb187c66f649" +source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.1.0#4b89097d8791a7190d01de1c576979c93df0013d" dependencies = [ "article-core", "libc", @@ -7447,10 +7447,21 @@ dependencies = [ "url", ] +[[package]] +name = "octosense-app-contract" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df5ab53906a90e05be4e32693728539486f404bd8639d2782534338631200bf" +dependencies = [ + "blake3", + "serde", + "serde_json", +] + [[package]] name = "octosense-app-hub" version = "0.1.0" -source = "git+https://www.github.com/OctoSense-org/OctoSense-App-Hub?rev=2a3d84b32ff6a12c6955decac2e987c3161a2d32#2a3d84b32ff6a12c6955decac2e987c3161a2d32" +source = "git+https://github.com/OctoSense-org/OctoSense-App-Hub?rev=58c3c8aed8fc811a16d67c5f784a8a44214ff876#58c3c8aed8fc811a16d67c5f784a8a44214ff876" dependencies = [ "base64 0.22.1", "blake3", @@ -7466,7 +7477,7 @@ dependencies = [ [[package]] name = "octosense-app-hub-app" version = "0.1.0" -source = "git+https://www.github.com/OctoSense-org/OctoSense-App-Hub?rev=2a3d84b32ff6a12c6955decac2e987c3161a2d32#2a3d84b32ff6a12c6955decac2e987c3161a2d32" +source = "git+https://github.com/OctoSense-org/OctoSense-App-Hub?rev=58c3c8aed8fc811a16d67c5f784a8a44214ff876#58c3c8aed8fc811a16d67c5f784a8a44214ff876" dependencies = [ "makepad-app-module", "makepad-widgets", @@ -7494,10 +7505,10 @@ dependencies = [ [[package]] name = "octosense-app-policy" version = "0.1.0" -source = "git+https://www.github.com/OctoSense-org/OctoSense-App-Hub?rev=2a3d84b32ff6a12c6955decac2e987c3161a2d32#2a3d84b32ff6a12c6955decac2e987c3161a2d32" +source = "git+https://github.com/OctoSense-org/OctoSense-App-Hub?rev=58c3c8aed8fc811a16d67c5f784a8a44214ff876#58c3c8aed8fc811a16d67c5f784a8a44214ff876" dependencies = [ - "blake3", "makepad-widgets", + "octosense-app-contract", "serde", "serde_json", ] @@ -7515,7 +7526,7 @@ dependencies = [ [[package]] name = "octosense-appstore" version = "0.1.0" -source = "git+https://www.github.com/OctoSense-org/OctoSense-App-Hub?rev=2a3d84b32ff6a12c6955decac2e987c3161a2d32#2a3d84b32ff6a12c6955decac2e987c3161a2d32" +source = "git+https://github.com/OctoSense-org/OctoSense-App-Hub?rev=58c3c8aed8fc811a16d67c5f784a8a44214ff876#58c3c8aed8fc811a16d67c5f784a8a44214ff876" dependencies = [ "makepad-app-module", "makepad-widgets", @@ -7668,6 +7679,7 @@ dependencies = [ "octoscript-makepad", "octoscript-ui-l0", "octosense-ai-host", + "octosense-app-contract", "octosense-app-hub", "octosense-app-hub-app", "octosense-app-policy", @@ -8826,8 +8838,8 @@ dependencies = [ [[package]] name = "rinx" -version = "1.0.2" -source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.0.2#5a9e2af2433a04129f2ad0c11acafb187c66f649" +version = "1.1.0" +source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.1.0#4b89097d8791a7190d01de1c576979c93df0013d" dependencies = [ "anyhow", "arboard", @@ -8866,8 +8878,8 @@ dependencies = [ "octoscript-makepad", "octoscript-ui-l0", "octoscript-widgets", + "octosense-app-contract", "octosense-app-peers", - "octosense-app-policy", "pulldown-cmark 0.13.4", "rand 0.8.8", "rangemap", @@ -8901,19 +8913,23 @@ dependencies = [ [[package]] name = "rinx-miniapp-catalog" version = "0.1.0" -source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.0.2#5a9e2af2433a04129f2ad0c11acafb187c66f649" +source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.1.0#4b89097d8791a7190d01de1c576979c93df0013d" dependencies = [ - "octosense-app-hub", - "octosense-app-policy", + "base64 0.22.1", + "ed25519-dalek", + "hex", + "octosense-app-contract", + "rand_core 0.9.5", "serde", "serde_json", + "ureq 3.4.2", "uuid", ] [[package]] name = "rinx-miniapp-core" version = "0.1.0" -source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.0.2#5a9e2af2433a04129f2ad0c11acafb187c66f649" +source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.1.0#4b89097d8791a7190d01de1c576979c93df0013d" dependencies = [ "serde", "serde_json", @@ -8922,9 +8938,9 @@ dependencies = [ [[package]] name = "rinx-system-apps" version = "0.1.0" -source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.0.2#5a9e2af2433a04129f2ad0c11acafb187c66f649" +source = "git+https://github.com/hagency-org/Rinx.git?tag=v1.1.0#4b89097d8791a7190d01de1c576979c93df0013d" dependencies = [ - "octosense-app-policy", + "octosense-app-contract", "serde", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 88880c7b..61687942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,12 +62,12 @@ authors = ["octos-app contributors"] # Reference and AppCard. A git source shares its repository's revision with # the pins below; tools/native_apps.py writes this block and checks that. # BEGIN native-apps: pins (generated by tools/native_apps.py from native-apps.json; do not edit) -rinx = { git = "https://github.com/hagency-org/Rinx.git", tag = "v1.0.2", default-features = false, features = ["octosense-module"] } +rinx = { git = "https://github.com/hagency-org/Rinx.git", tag = "v1.1.0", default-features = false, features = ["octosense-module"] } octosense-reference = { path = "apps/reference" } makepad-sheets = { git = "https://github.com/OctoSense-org/makepad.git", rev = "e29a0eaa68781231eb9ec582e5f02648c6dad7e9", default-features = false } makepad-terminal = { git = "https://github.com/OctoSense-org/makepad.git", rev = "e29a0eaa68781231eb9ec582e5f02648c6dad7e9" } octosense-appcard = { path = "apps/appcard/module" } -octosense-app-hub-app = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } +octosense-app-hub-app = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "58c3c8aed8fc811a16d67c5f784a8a44214ff876" } # END native-apps: pins # --- Makepad (OctoSense-org/makepad), the revision Octoscript-Makepad's # runtime.json pins (native-runtime.lock.json); resolved to .sources/makepad. @@ -96,12 +96,17 @@ octoscript-makepad = { git = "https://github.com/OctoSense-org/Octoscript-Makepa octoscript-node = { git = "https://github.com/OctoSense-org/Octoscript-Makepad.git", rev = "e0df0b5177f9cb0d8ad0ad0225d40ebcf8389a1b" } # --- App Hub's host service registry the Mail and llm services register # into; the same revision as App Hub's app crate (a native app, above). -octosense-appstore = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } +octosense-appstore = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "58c3c8aed8fc811a16d67c5f784a8a44214ff876" } # The app policy (resolution, isolate settings, the Splash adapter) and the # Hub client the Card runner resolves an app's policy with; the shell's # glance tiles run cards under it (crates/shell/src/glance_card.rs). -octosense-app-policy = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } -octosense-app-hub = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } +octosense-app-policy = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "58c3c8aed8fc811a16d67c5f784a8a44214ff876" } +octosense-app-hub = { git = "https://github.com/OctoSense-org/OctoSense-App-Hub", rev = "58c3c8aed8fc811a16d67c5f784a8a44214ff876" } +# The app contract (ADR 0005), from crates.io by version: App Hub, the shell +# and Rinx all depend on `octosense-app-contract = "1"`, so Cargo links ONE +# 1.x for the whole build (Cargo.lock pins the exact version OctoSense ships) +# and App Hub and Rinx no longer need the same App Hub commit. +octosense-app-contract = "1" # --- octos: ONE source for every octos crate (octos-org/octos main at the # merge of octos#2645, Bing News's Chinese results with publisher links; # after #2644, the other lane's running turn in shared history, #2637, the @@ -248,17 +253,6 @@ octosense-app-peers = { path = "crates/app-peers" } octosense-kernel = { path = "crates/kernel" } octosense-llm-config = { path = "apps/ai-providers/config" } -# One App Hub: Rinx v1.0.2 names App Hub at an older rev (0f332112) than the -# pin above (its mini-app catalog and system apps take app-policy and the -# hub client). A patch cannot point a source at another rev of itself, so -# every App Hub crate resolves through the www host alias of the same -# repository, at the pin. Drop this once Rinx pins the same App Hub commit. -[patch."https://github.com/OctoSense-org/OctoSense-App-Hub"] -octosense-app-hub = { git = "https://www.github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } -octosense-app-policy = { git = "https://www.github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } -octosense-appstore = { git = "https://www.github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } -octosense-app-hub-app = { git = "https://www.github.com/OctoSense-org/OctoSense-App-Hub", rev = "2a3d84b32ff6a12c6955decac2e987c3161a2d32" } - # Rinx's Matrix stack: the patched ruma its own workspace uses (Rinx's # [patch] sections do not reach a consumer workspace). [patch."https://github.com/ruma/ruma"] diff --git a/apps/ai-providers/host-service/tests/complete.rs b/apps/ai-providers/host-service/tests/complete.rs index 779ff5bd..6ce22cd0 100644 --- a/apps/ai-providers/host-service/tests/complete.rs +++ b/apps/ai-providers/host-service/tests/complete.rs @@ -110,7 +110,7 @@ impl Rig { fn call(&self, app: &str, method: &str, args: Value) -> Result { let heap = NEXT.fetch_add(1, Ordering::Relaxed); - let call = ServiceCall { app_id: app.into(), service: format!("model.{method}"), args, from_sheet: false, host_dir: self.dir.join(".host") }; + let call = ServiceCall { app_id: app.into(), service: format!("model.{method}"), args, from_sheet: false, may_prompt: true, host_dir: self.dir.join(".host") }; dispatch(call, heap, 1, &mut NoSheets); let deadline = Instant::now() + Duration::from_secs(10); loop { diff --git a/apps/ai-providers/host-service/tests/service.rs b/apps/ai-providers/host-service/tests/service.rs index 79c89884..3be8c13d 100644 --- a/apps/ai-providers/host-service/tests/service.rs +++ b/apps/ai-providers/host-service/tests/service.rs @@ -71,7 +71,7 @@ impl Rig { fn send(&mut self, app: &str, service: &str, args: Value, from_sheet: bool) -> usize { let heap = NEXT.fetch_add(1, Ordering::Relaxed); - let call = ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet, host_dir: self.dir.clone() }; + let call = ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet, may_prompt: true, host_dir: self.dir.clone() }; dispatch(call, heap, 1, &mut self.host); heap } diff --git a/apps/mail/host-service/src/lib.rs b/apps/mail/host-service/src/lib.rs index 9e50baac..b2bcdb57 100644 --- a/apps/mail/host-service/src/lib.rs +++ b/apps/mail/host-service/src/lib.rs @@ -792,7 +792,7 @@ mod tests { /// isolate of its own, so answers cannot cross. fn send(dir: &Path, app: &str, service: &str, args: Value, from_sheet: bool, host: &mut Host) -> usize { let heap = NEXT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - let call = ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet, host_dir: dir.into() }; + let call = ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet, may_prompt: true, host_dir: dir.into() }; octosense_appstore::services::dispatch(call, heap, 1, host); heap } diff --git a/apps/news/host-service/tests/bundle.rs b/apps/news/host-service/tests/bundle.rs index b449ba16..b9b739ef 100644 --- a/apps/news/host-service/tests/bundle.rs +++ b/apps/news/host-service/tests/bundle.rs @@ -64,6 +64,7 @@ fn an_unregistered_service_answers_with_an_error() { service: "news.list".into(), args: json!({"feed": "hn", "current": true}), from_sheet: false, + may_prompt: true, host_dir: std::env::temp_dir(), }; dispatch(call, 51_000, 1, &mut NoSheets); diff --git a/apps/news/host-service/tests/service.rs b/apps/news/host-service/tests/service.rs index 246abfee..5e36e101 100644 --- a/apps/news/host-service/tests/service.rs +++ b/apps/news/host-service/tests/service.rs @@ -343,7 +343,7 @@ impl ServiceHost for NoSheets { fn ask(app: &str, dir: &std::path::Path, service: &str, args: Value) -> Result { let heap = NEXT.fetch_add(1, Ordering::SeqCst); - let call = ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet: false, host_dir: dir.to_path_buf() }; + let call = ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet: false, may_prompt: true, host_dir: dir.to_path_buf() }; dispatch(call, heap, 1, &mut NoSheets); for _ in 0..500 { if let Some((_, _, answer)) = take_replies_for(&[heap]).pop() { diff --git a/crates/ai-host/src/contained/tests.rs b/crates/ai-host/src/contained/tests.rs index 6465b9db..b5c92ec2 100644 --- a/crates/ai-host/src/contained/tests.rs +++ b/crates/ai-host/src/contained/tests.rs @@ -197,6 +197,7 @@ fn ask(app: &str, service: &str, args: Value, from_sheet: bool) -> Result` feature. Rinx gets the assistant only through the injected @@ -84,6 +88,7 @@ octosense-app-hub-app = { workspace = true } octosense-appstore = { workspace = true } octosense-app-policy = { workspace = true } octosense-app-hub = { workspace = true } +octosense-app-contract = { workspace = true } octosense-mail-service = { workspace = true } octosense-news-service = { workspace = true } octosense-ai-host = { workspace = true, features = ["llm"] } @@ -106,7 +111,7 @@ app-reference = ["dep:octosense-reference"] app-sheets = ["dep:makepad-sheets"] app-terminal = ["dep:makepad-terminal"] app-appcard = ["dep:octosense-appcard", "octos-core"] -app-hub = ["dep:octosense-app-hub-app", "dep:octosense-appstore", "dep:octosense-app-policy", "dep:octosense-app-hub", "dep:octosense-mail-service", "dep:octosense-news-service", "octosense-ai-host/llm"] +app-hub = ["dep:octosense-app-hub-app", "dep:octosense-appstore", "dep:octosense-app-policy", "dep:octosense-app-hub", "dep:octosense-app-contract", "dep:octosense-mail-service", "dep:octosense-news-service", "octosense-ai-host/llm"] # END native-apps: features # The aichat module, seated in the pane in-process. app-aichat = ["dep:makepad-aichat"] diff --git a/crates/shell/src/apps.rs b/crates/shell/src/apps.rs index 638561a5..2c439802 100644 --- a/crates/shell/src/apps.rs +++ b/crates/shell/src/apps.rs @@ -306,7 +306,7 @@ pub fn script_agent_app(manifest_path: &Path, id: &str, name: &str) -> Option bool { - let Ok(parsed) = octosense_app_policy::AppManifest::parse(manifest) else { return false }; + let Ok(parsed) = octosense_app_contract::AppManifest::parse(manifest) else { return false }; matches!(octosense_app_policy::AgentBundle::load(bundle, &parsed), Ok(Some(_))) } diff --git a/crates/shell/src/glance.rs b/crates/shell/src/glance.rs index 0fb43a5e..fbb2aae8 100644 --- a/crates/shell/src/glance.rs +++ b/crates/shell/src/glance.rs @@ -721,7 +721,7 @@ mod tests { fn close_sheet(&mut self) {} } register(); - let call = |app: &str, service: &str, args: Value| ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet: false, host_dir: std::env::temp_dir() }; + let call = |app: &str, service: &str, args: Value| ServiceCall { app_id: app.into(), service: service.into(), args, from_sheet: false, may_prompt: true, host_dir: std::env::temp_dir() }; let from_sheet = |app: &str, service: &str, args: Value| ServiceCall { from_sheet: true, ..call(app, service, args) }; let mut spoof = args("dispatch-test"); spoof["app"] = json!("os.mail"); diff --git a/crates/shell/src/glance_card.rs b/crates/shell/src/glance_card.rs index 5228d7a8..d39b3228 100644 --- a/crates/shell/src/glance_card.rs +++ b/crates/shell/src/glance_card.rs @@ -276,7 +276,7 @@ pub fn app_isolate(cx: &Cx, app: &str) -> Result octosense_appstore::system::prepare(&root, &system)?.1, None => { let anchor = std::env::var("OCTOSENSE_HUB_ANCHOR").unwrap_or_else(|_| octosense_appstore::DEFAULT_ANCHOR.to_string()); - let mut store = octosense_app_hub::Store::new(&anchor, &root, octosense_app_policy::HostLimits::default()); + let mut store = octosense_app_hub::Store::new(&anchor, &root, octosense_app_contract::HostLimits::default()); let catalog = std::fs::read_to_string(root.join("catalog.json")).unwrap_or_default(); store.accept_catalog(&catalog).map_err(|e| format!("no verified catalog on this device ({e})"))?; store.may_run(app)? @@ -328,7 +328,7 @@ mod tests { let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(&dir).unwrap(); std::fs::write(dir.join("main.splash"), "View{}").unwrap(); - let digest = octosense_app_policy::digest_dir(&dir).unwrap(); + let digest = octosense_app_contract::digest_dir(&dir).unwrap(); let manifest = format!( r#"{{"schema":1,"id":"{id}","version":"1","name":"Glance test","integrity":{{"bundle_blake3":"{digest}"}},"capabilities":["storage","glance"]}}"# ); diff --git a/crates/shell/src/host_tools/scenario_tests.rs b/crates/shell/src/host_tools/scenario_tests.rs index 7b7edd56..66066e3b 100644 --- a/crates/shell/src/host_tools/scenario_tests.rs +++ b/crates/shell/src/host_tools/scenario_tests.rs @@ -169,7 +169,7 @@ fn register_app(dir: &Path) { std::fs::create_dir_all(&bundle).unwrap(); std::fs::write(bundle.join("main.splash"), "View{}").unwrap(); std::fs::write(bundle.join("tools.json"), serde_json::to_vec_pretty(&tools_json()).unwrap()).unwrap(); - let digest = octosense_app_policy::digest_dir(&bundle).unwrap(); + let digest = octosense_app_contract::digest_dir(&bundle).unwrap(); let manifest = json!({ "schema": 1, "id": APP, "version": "1", "name": LABEL, "integrity": {"bundle_blake3": digest}, diff --git a/crates/shell/src/host_tools/script_apps.rs b/crates/shell/src/host_tools/script_apps.rs index 80c80e6c..2bd06526 100644 --- a/crates/shell/src/host_tools/script_apps.rs +++ b/crates/shell/src/host_tools/script_apps.rs @@ -31,7 +31,8 @@ use std::sync::{Arc, Mutex}; use serde_json::{json, Value}; use crate::ai_host::app_peers::host_tools::{HostToolCall, ToolExecutor, ToolOutcome, ToolReply}; -use octosense_app_policy::{AgentBundle, AppManifest, ImplementedBy, ToolSpec, MANIFEST_FILE}; +use octosense_app_contract::{AppManifest, MANIFEST_FILE}; +use octosense_app_policy::{AgentBundle, ImplementedBy, ToolSpec}; use octosense_appstore::services::{ServiceCall, ServiceHost}; /// What one script app's bundle gives the host. @@ -188,7 +189,7 @@ impl ToolExecutor for HostServiceExecutor { } let key = NEXT_KEY.fetch_add(1, std::sync::atomic::Ordering::Relaxed); WAITING.lock().unwrap_or_else(|e| e.into_inner()).get_or_insert_with(HashMap::new).insert(key, Waiting { reply }); - let service_call = ServiceCall { app_id: self.app.clone(), service: call.name.clone(), args: call.args.clone(), from_sheet: false, host_dir: self.host_dir.clone() }; + let service_call = ServiceCall { app_id: self.app.clone(), service: call.name.clone(), args: call.args.clone(), from_sheet: false, may_prompt: true, host_dir: self.host_dir.clone() }; octosense_appstore::services::dispatch(service_call, key, 0, &mut NoSheet); } @@ -239,7 +240,7 @@ pub(crate) mod tests { } let mut manifest: Value = serde_json::from_str(&std::fs::read_to_string(dir.join(MANIFEST_FILE)).unwrap()).unwrap(); edit(&dir, &mut manifest); - manifest["integrity"]["bundle_blake3"] = json!(octosense_app_policy::digest_dir(&dir).unwrap()); + manifest["integrity"]["bundle_blake3"] = json!(octosense_app_contract::digest_dir(&dir).unwrap()); std::fs::write(dir.join(MANIFEST_FILE), serde_json::to_vec_pretty(&manifest).unwrap()).unwrap(); dir } diff --git a/native-apps.json b/native-apps.json index 61e6e9c7..67ddfe12 100644 --- a/native-apps.json +++ b/native-apps.json @@ -4,7 +4,7 @@ { "id": "rinx", "crate": "rinx", - "source": { "git": "https://github.com/hagency-org/Rinx.git", "tag": "v1.0.2", "rev": "5a9e2af2433a04129f2ad0c11acafb187c66f649", "local": null }, + "source": { "git": "https://github.com/hagency-org/Rinx.git", "tag": "v1.1.0", "rev": "4b89097d8791a7190d01de1c576979c93df0013d", "local": null }, "module": "rinx::module::RINX_MODULE", "bin": null, "default_features": false, @@ -99,12 +99,12 @@ "id": "apphub", "feature": "app-hub", "crate": "octosense-app-hub-app", - "source": { "git": "https://github.com/OctoSense-org/OctoSense-App-Hub", "rev": "2a3d84b32ff6a12c6955decac2e987c3161a2d32", "local": null }, + "source": { "git": "https://github.com/OctoSense-org/OctoSense-App-Hub", "rev": "58c3c8aed8fc811a16d67c5f784a8a44214ff876", "local": null }, "module": ["octosense_app_hub_app::APP_HUB_MODULE", "octosense_app_hub_app::CARD_MODULE"], "bin": null, "default_features": true, "crate_features": [], - "implies": ["dep:octosense-appstore", "dep:octosense-app-policy", "dep:octosense-app-hub", "dep:octosense-mail-service", "dep:octosense-news-service", "octosense-ai-host/llm"], + "implies": ["dep:octosense-appstore", "dep:octosense-app-policy", "dep:octosense-app-hub", "dep:octosense-app-contract", "dep:octosense-mail-service", "dep:octosense-news-service", "octosense-ai-host/llm"], "hosting": { "macos": "module", "windows": "module", "linux": "module", "android": "module", "ios": "module", "ohos": "module" }, "shells": { "desktop": "default", "phone": "default" }, "native_mobile": "always",