Skip to main content

qualia_client_core/api/
daemon.rs

1//! Daemon port + app session tokens
2
3#![allow(non_snake_case)]
4
5use super::*;
6
7use crate::qapp_paths::qapps_dir;
8use std::sync::atomic::{AtomicU16, Ordering};
9
10static ACTIVE_DAEMON_PORT: AtomicU16 = AtomicU16::new(0);
11
12/// Records the loopback port chosen when the graph daemon last started.
13pub fn set_active_daemon_port(port: u16) {
14    ACTIVE_DAEMON_PORT.store(port, Ordering::SeqCst);
15}
16
17/// Returns the active daemon port when known, otherwise the configured default.
18pub fn get_active_daemon_port() -> u16 {
19    let active = ACTIVE_DAEMON_PORT.load(Ordering::SeqCst);
20    if active != 0 {
21        return active;
22    }
23    crate::state::APP_STATE
24        .get()
25        .map(|state| state.config.lock().unwrap().daemon_port)
26        .unwrap_or(4242)
27}
28
29/// Issues a signed semantic app token scoped to the installed app's manifest shapes.
30pub fn build_anatomy_graph_context_json(
31    qapp_name: String,
32    user_prompt: String,
33    agent_reply: String,
34) -> Result<String, String> {
35    crate::anatomy_context::build_anatomy_graph_context_json(qapp_name, user_prompt, agent_reply)
36}
37
38pub fn build_anatomy_graph_context_json_with_dicom(
39    qapp_name: String,
40    user_prompt: String,
41    agent_reply: String,
42    dicom_file_path: Option<String>,
43) -> Result<String, String> {
44    crate::anatomy_context::build_anatomy_graph_context_json_with_dicom(
45        qapp_name,
46        user_prompt,
47        agent_reply,
48        dicom_file_path,
49    )
50}
51
52pub fn parse_dicom_metadata_json(file_path: String) -> Result<String, String> {
53    crate::anatomy_context::parse_dicom_metadata_json(file_path)
54}
55
56pub fn build_dicom_overlay_spec_json(file_path: String) -> Result<String, String> {
57    crate::anatomy_context::build_dicom_overlay_spec_json(file_path)
58}
59
60pub fn submit_dicom_ingest(file_path: String, patient_did_hash: u64) -> Result<u64, String> {
61    crate::qapp_api::submit_dicom_ingest(file_path, patient_did_hash)
62}
63
64pub fn dicom_ingest_status(job_id: u64) -> u8 {
65    crate::qapp_api::dicom_ingest_status(job_id)
66}
67
68pub fn execute_dicom_volume_query(
69    patient_did_hash: u64,
70    series_hash: u64,
71) -> Result<Vec<u8>, String> {
72    crate::qapp_api::execute_dicom_volume_query(patient_did_hash, series_hash)
73}
74
75pub fn issue_qapp_session_token(qapp_name: &str) -> Result<String, String> {
76    let state = crate::state::APP_STATE.get().unwrap();
77    let data_dir = state.config.lock().unwrap().storage_path.clone();
78    let qapp_dir = qapps_dir(&data_dir).join(qapp_name);
79    if !qapp_dir.exists() {
80        return Err(format!("Qapp directory not found: {qapp_name}"));
81    }
82
83    let manifest = load_qapp_package_from_dir(&qapp_dir)?;
84    let qapp_did = format!(
85        "did:qualia:qapp:{}",
86        manifest.name.to_lowercase().replace(' ', "-")
87    );
88    let vault = state.key_vault.lock().unwrap();
89    vault.issue_qapp_token(
90        &qapp_did,
91        "localhost",
92        std::time::SystemTime::now()
93            .duration_since(std::time::UNIX_EPOCH)
94            .unwrap()
95            .as_secs()
96            + 86400,
97        &uuid::Uuid::new_v4().to_string(),
98        manifest.required_shapes.clone(),
99        qualia_core_db::identity::key_vault::SubgraphLayer::Professional,
100    )
101}