helix/
lib.rs

1pub mod dna {
2    pub mod atp;
3    pub mod bch;
4    pub mod cmd;
5    pub mod exp;
6    pub mod ffi;
7    pub mod hel;
8    pub mod map;
9    pub mod mds;
10    pub mod ngs;
11    pub mod ops;
12    pub mod out;
13    pub mod tst;
14    pub mod compiler;
15}
16pub use dna::atp;
17pub use dna::bch;
18pub use dna::cmd;
19pub use dna::exp;
20pub use dna::ffi;
21pub use dna::hel;
22pub use dna::map;
23pub use dna::mds;
24pub use dna::ngs;
25pub use dna::ops;
26pub use dna::out;
27pub use dna::tst;
28pub use dna::compiler;
29pub use dna::compiler::Compiler;
30pub use dna::mds::optimizer::OptimizationLevel;
31pub use dna::hel::dna_hlx::Hlx;
32pub use dna::atp::value::Value as DnaValue;
33pub async fn xlh(hlx: &Hlx, operator: &str, params: &str) -> Result<crate::dna::atp::value::Value, crate::hel::error::HlxError> {
34    hlx.execute_operator(operator, params).await
35} 
36#[cfg(test)]
37mod tests;
38#[cfg(test)]
39mod bch;
40
41#[cfg(test)]
42#[path = "dna/tst/integration_tests.rs"]
43mod integration_tests;
44
45pub use crate::dna::atp::types::{
46    HelixConfig, ProjectConfig, AgentConfig, WorkflowConfig, MemoryConfig, ContextConfig,
47    CrewConfig, PipelineConfig, RetryConfig, TriggerConfig, StepConfig, Value,
48    load_default_config, DataFormat, TrainingFormat, GenericJSONDataset, TrainingDataset,
49    TrainingSample, AlgorithmFormat,
50};
51pub use crate::dna::out::hlxb_config_format::{
52    HlxbWriter, HlxbReader, HlxbHeader, HLXB_MAGIC, HLXB_VERSION,
53};
54pub use crate::dna::atp::ast::{
55    HelixAst, Declaration, Expression, Statement, AgentDecl, WorkflowDecl, MemoryDecl,
56    ContextDecl, CrewDecl, PipelineDecl,
57};
58pub use crate::dna::atp::lexer::{Token, SourceLocation};
59pub use crate::dna::atp::parser::{Parser, ParseError};
60pub use crate::dna::mds::semantic::{SemanticAnalyzer, SemanticError};
61pub use crate::dna::mds::codegen::{CodeGenerator, HelixIR};
62pub use crate::dna::atp::types::HelixLoader;
63pub use crate::dna::mds::server::{HelixServer, ServerConfig};
64use std::path::Path;
65type ParseResult<T> = Result<T, ParseError>;
66#[cfg(feature = "js")]
67use napi::bindgen_prelude::*;
68#[cfg(feature = "js")]
69use napi_derive::napi;
70#[cfg(feature = "js")]
71#[derive(Debug, Clone)]
72pub struct NapiStringError(pub String);
73#[cfg(feature = "js")]
74impl AsRef<str> for NapiStringError {
75    fn as_ref(&self) -> &str {
76        &self.0
77    }
78}
79#[cfg(feature = "js")]
80impl From<String> for NapiStringError {
81    fn from(s: String) -> Self {
82        NapiStringError(s)
83    }
84}
85#[cfg(feature = "js")]
86impl std::fmt::Display for NapiStringError {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(f, "{}", self.0)
89    }
90}
91#[cfg(feature = "js")]
92impl std::error::Error for NapiStringError {}
93#[cfg(feature = "js")]
94impl From<NapiStringError> for napi::Error<NapiStringError> {
95    fn from(err: NapiStringError) -> Self {
96        napi::Error::new(err, napi::Status::GenericFailure)
97    }
98}
99#[cfg(feature = "js")]
100type ConfigResult<T> = Result<T, NapiStringError>;
101#[cfg(not(feature = "js"))]
102type ConfigResult<T> = Result<T, String>;
103pub fn parse(source: &str) -> std::result::Result<HelixAst, ParseError> {
104    parse_with_locations(source).or_else(|_| parse_legacy(source))
105}
106pub fn parse_with_locations(source: &str) -> std::result::Result<HelixAst, ParseError> {
107    use crate::dna::atp::lexer::{tokenize_with_locations, SourceMap};
108    let tokens_with_loc = match tokenize_with_locations(source) {
109        Ok(tokens) => tokens,
110        Err(e) => {
111            return Err(ParseError {
112                message: format!("Lexer error: {}", e),
113                location: None,
114                token_index: 0,
115                expected: None,
116                found: String::new(),
117                context: String::new(),
118            });
119        }
120    };
121    let source_map = SourceMap {
122        tokens: tokens_with_loc.clone(),
123        source: source.to_string(),
124    };
125    let mut parser = Parser::new_with_source_map(source_map);
126    match parser.parse() {
127        Ok(ast) => Ok(ast),
128        Err(msg) => {
129            Err(ParseError {
130                message: msg,
131                location: None,
132                token_index: 0,
133                expected: None,
134                found: String::new(),
135                context: String::new(),
136            })
137        }
138    }
139}
140fn parse_legacy(source: &str) -> std::result::Result<HelixAst, ParseError> {
141    let tokens = match crate::dna::atp::lexer::tokenize(source) {
142        Ok(tokens) => tokens,
143        Err(e) => {
144            return Err(ParseError {
145                message: format!("Lexer error: {}", e),
146                location: None,
147                token_index: 0,
148                expected: None,
149                found: String::new(),
150                context: String::new(),
151            });
152        }
153    };
154    let mut parser = Parser::new(tokens);
155    match parser.parse() {
156        Ok(ast) => Ok(ast),
157        Err(msg) => {
158            Err(ParseError {
159                message: msg,
160                location: None,
161                token_index: 0,
162                expected: None,
163                found: String::new(),
164                context: String::new(),
165            })
166        }
167    }
168}
169#[cfg(feature = "js")]
170pub fn parse_and_validate(
171    source: &str,
172) -> std::result::Result<HelixConfig, NapiStringError> {
173    let ast = match parse(source) {
174        Ok(ast) => ast,
175        Err(e) => return Err(NapiStringError(e.to_string())),
176    };
177    validate(&ast)?;
178    ast_to_config(ast)
179}
180#[cfg(not(feature = "js"))]
181pub fn parse_and_validate(source: &str) -> std::result::Result<HelixConfig, String> {
182    let ast = match parse(source) {
183        Ok(ast) => ast,
184        Err(e) => return Err(e.to_string()),
185    };
186    validate(&ast)?;
187    ast_to_config(ast)
188}
189#[cfg(feature = "js")]
190pub fn validate(ast: &HelixAst) -> std::result::Result<(), NapiStringError> {
191    let mut analyzer = SemanticAnalyzer::new();
192    match analyzer.analyze(ast) {
193        Ok(()) => Ok(()),
194        Err(errors) => {
195            Err(
196                NapiStringError(
197                    errors
198                        .iter()
199                        .map(|e| format!("{:?}", e))
200                        .collect::<Vec<_>>()
201                        .join("\n"),
202                ),
203            )
204        }
205    }
206}
207#[cfg(not(feature = "js"))]
208pub fn validate(ast: &HelixAst) -> std::result::Result<(), String> {
209    let mut analyzer = SemanticAnalyzer::new();
210    match analyzer.analyze(ast) {
211        Ok(()) => Ok(()),
212        Err(errors) => {
213            Err(errors.iter().map(|e| format!("{:?}", e)).collect::<Vec<_>>().join("\n"))
214        }
215    }
216}
217#[cfg(feature = "js")]
218pub fn ast_to_config(
219    ast: HelixAst,
220) -> std::result::Result<HelixConfig, NapiStringError> {
221    let loader = crate::dna::atp::types::HelixLoader::new();
222    match loader.ast_to_config(ast) {
223        Ok(config) => Ok(config),
224        Err(e) => Err(NapiStringError(e.to_string())),
225    }
226}
227#[cfg(not(feature = "js"))]
228pub fn ast_to_config(ast: HelixAst) -> std::result::Result<HelixConfig, String> {
229    let loader = crate::dna::atp::types::HelixLoader::new();
230    match loader.ast_to_config(ast) {
231        Ok(config) => Ok(config),
232        Err(e) => Err(e.to_string()),
233    }
234}
235#[cfg(feature = "js")]
236pub fn load_file<P: AsRef<Path>>(
237    path: P,
238) -> std::result::Result<HelixConfig, NapiStringError> {
239    let content = match std::fs::read_to_string(path) {
240        Ok(content) => content,
241        Err(e) => return Err(NapiStringError(format!("Failed to read file: {}", e))),
242    };
243    parse_and_validate(&content)
244}
245#[cfg(not(feature = "js"))]
246pub fn load_file<P: AsRef<Path>>(path: P) -> std::result::Result<HelixConfig, String> {
247    let content = match std::fs::read_to_string(path) {
248        Ok(content) => content,
249        Err(e) => return Err(format!("Failed to read file: {}", e)),
250    };
251    parse_and_validate(&content)
252}
253#[cfg(feature = "js")]
254pub fn load_directory<P: AsRef<Path>>(
255    path: P,
256) -> std::result::Result<Vec<HelixConfig>, NapiStringError> {
257    let mut configs = Vec::new();
258    let entries = match std::fs::read_dir(path) {
259        Ok(entries) => entries,
260        Err(e) => return Err(NapiStringError(format!("Failed to read directory: {}", e))),
261    };
262    for entry in entries {
263        let entry = match entry {
264            Ok(entry) => entry,
265            Err(e) => return Err(NapiStringError(format!("Failed to read entry: {}", e))),
266        };
267        let path = entry.path();
268        if path.extension().and_then(|s| s.to_str()) == Some("HELIX") {
269            let config = match load_file(&path) {
270                Ok(config) => config,
271                Err(e) => return Err(e),
272            };
273            configs.push(config);
274        }
275    }
276    Ok(configs)
277}
278#[cfg(not(feature = "js"))]
279pub fn load_directory<P: AsRef<Path>>(
280    path: P,
281) -> std::result::Result<Vec<HelixConfig>, String> {
282    let mut configs = Vec::new();
283    let entries = match std::fs::read_dir(path) {
284        Ok(entries) => entries,
285        Err(e) => return Err(format!("Failed to read directory: {}", e)),
286    };
287    for entry in entries {
288        let entry = match entry {
289            Ok(entry) => entry,
290            Err(e) => return Err(format!("Failed to read entry: {}", e)),
291        };
292        let path = entry.path();
293        if path.extension().and_then(|s| s.to_str()) == Some("HELIX") {
294            let config = match load_file(&path) {
295                Ok(config) => config,
296                Err(e) => return Err(e),
297            };
298            configs.push(config);
299        }
300    }
301    Ok(configs)
302}
303pub fn pretty_print(ast: &HelixAst) -> String {
304    let mut printer = crate::dna::atp::ast::AstPrettyPrinter::new();
305    printer.print(ast)
306}
307#[cfg(feature = "php")]
308use std::ffi::{CStr, CString};
309#[cfg(feature = "php")]
310use std::os::raw::c_char;
311/// Execute Helix code using FFI
312#[cfg(feature = "php")]
313#[no_mangle]
314pub extern "C" fn helix_execute_ffi(code_ptr: *const c_char) -> *mut c_char {
315    if code_ptr.is_null() {
316        return std::ptr::null_mut();
317    }
318    let code = unsafe { CStr::from_ptr(code_ptr) };
319    let code_str = match code.to_str() {
320        Ok(s) => s,
321        Err(_) => {
322            let error_str = "Error: Invalid UTF-8 in code string";
323            if let Ok(cstring) = CString::new(error_str) {
324                return cstring.into_raw();
325            }
326            return std::ptr::null_mut();
327        }
328    };
329    let runtime = match tokio::runtime::Runtime::new() {
330        Ok(rt) => rt,
331        Err(e) => {
332            let error_str = format!("Error: Failed to create runtime: {}", e);
333            if let Ok(cstring) = CString::new(error_str) {
334                return cstring.into_raw();
335            }
336            return std::ptr::null_mut();
337        }
338    };
339    let result = runtime
340        .block_on(async {
341            let mut hlx = match crate::dna_hlx::Hlx::new().await {
342                Ok(h) => h,
343                Err(e) => return Err(format!("Failed to initialize Helix: {}", e)),
344            };
345            hlx.execute(code_str).await.map_err(|e| format!("Execution error: {}", e))
346        });
347    match result {
348        Ok(value) => {
349            let result_str = format!("{}", value);
350            match CString::new(result_str) {
351                Ok(cstring) => cstring.into_raw(),
352                Err(e) => {
353                    let error_str = format!(
354                        "Error: Failed to create result string: {}", e
355                    );
356                    if let Ok(cstring) = CString::new(error_str) {
357                        cstring.into_raw()
358                    } else {
359                        std::ptr::null_mut()
360                    }
361                }
362            }
363        }
364        Err(e) => {
365            match CString::new(e) {
366                Ok(cstring) => cstring.into_raw(),
367                Err(_) => {
368                    let error_str = "Error: Failed to create error string";
369                    if let Ok(cstring) = CString::new(error_str) {
370                        cstring.into_raw()
371                    } else {
372                        std::ptr::null_mut()
373                    }
374                }
375            }
376        }
377    }
378}
379/// Parse Helix code using FFI
380#[cfg(feature = "php")]
381#[no_mangle]
382pub extern "C" fn helix_parse_ffi(code_ptr: *const c_char) -> *mut c_char {
383    if code_ptr.is_null() {
384        return std::ptr::null_mut();
385    }
386    let code = unsafe { CStr::from_ptr(code_ptr) };
387    let code_str = match code.to_str() {
388        Ok(s) => s,
389        Err(_) => {
390            let error_str = "Error: Invalid UTF-8 in code string";
391            if let Ok(cstring) = CString::new(error_str) {
392                return cstring.into_raw();
393            }
394            return std::ptr::null_mut();
395        }
396    };
397    let dispatcher = crate::dispatch::HelixDispatcher::new();
398    let result = dispatcher.parse_only(code_str);
399    match result {
400        Ok(ast) => {
401            let ast_json = match serde_json::to_string_pretty(&ast) {
402                Ok(json) => json,
403                Err(_) => format!("{:?}", ast),
404            };
405            let result_str = format!("Parsed AST:\n{}", ast_json);
406            match CString::new(result_str) {
407                Ok(cstring) => cstring.into_raw(),
408                Err(e) => {
409                    let error_str = format!("Error: Failed to create AST string: {}", e);
410                    if let Ok(cstring) = CString::new(error_str) {
411                        cstring.into_raw()
412                    } else {
413                        std::ptr::null_mut()
414                    }
415                }
416            }
417        }
418        Err(e) => {
419            let error_str = format!("Parse Error: {}", e);
420            match CString::new(error_str) {
421                Ok(cstring) => cstring.into_raw(),
422                Err(_) => {
423                    let error_str = "Error: Failed to create parse error string";
424                    if let Ok(cstring) = CString::new(error_str) {
425                        cstring.into_raw()
426                    } else {
427                        std::ptr::null_mut()
428                    }
429                }
430            }
431        }
432    }
433}
434/// Load and execute a Helix file using FFI
435#[cfg(feature = "php")]
436#[no_mangle]
437pub extern "C" fn helix_load_file_ffi(file_path_ptr: *const c_char) -> *mut c_char {
438    if file_path_ptr.is_null() {
439        return std::ptr::null_mut();
440    }
441    let file_path = unsafe { CStr::from_ptr(file_path_ptr) };
442    let file_path_str = match file_path.to_str() {
443        Ok(s) => s,
444        Err(_) => {
445            let error_str = "Error: Invalid UTF-8 in file path";
446            if let Ok(cstring) = CString::new(error_str) {
447                return cstring.into_raw();
448            }
449            return std::ptr::null_mut();
450        }
451    };
452    let runtime = match tokio::runtime::Runtime::new() {
453        Ok(rt) => rt,
454        Err(e) => {
455            let error_str = format!("Error: Failed to create runtime: {}", e);
456            if let Ok(cstring) = CString::new(error_str) {
457                return cstring.into_raw();
458            }
459            return std::ptr::null_mut();
460        }
461    };
462    let result = runtime
463        .block_on(async {
464            let content = std::fs::read_to_string(file_path_str)
465                .map_err(|e| format!("Failed to read file '{}': {}", file_path_str, e))?;
466            let mut hlx = crate::dna_hlx::Hlx::new()
467                .await
468                .map_err(|e| format!("Failed to initialize Helix: {}", e))?;
469            hlx.execute(&content).await.map_err(|e| format!("Execution error: {}", e))
470        });
471    match result {
472        Ok(value) => {
473            let result_str = format!(
474                "File '{}' executed successfully:\n{}", file_path_str, value
475            );
476            match CString::new(result_str) {
477                Ok(cstring) => cstring.into_raw(),
478                Err(e) => {
479                    let error_str = format!(
480                        "Error: Failed to create result string: {}", e
481                    );
482                    if let Ok(cstring) = CString::new(error_str) {
483                        cstring.into_raw()
484                    } else {
485                        std::ptr::null_mut()
486                    }
487                }
488            }
489        }
490        Err(e) => {
491            match CString::new(e) {
492                Ok(cstring) => cstring.into_raw(),
493                Err(_) => {
494                    let error_str = "Error: Failed to create error string";
495                    if let Ok(cstring) = CString::new(error_str) {
496                        cstring.into_raw()
497                    } else {
498                        std::ptr::null_mut()
499                    }
500                }
501            }
502        }
503    }
504}
505/// Free a C string allocated by the FFI functions
506#[cfg(feature = "php")]
507#[no_mangle]
508pub extern "C" fn helix_free_string(ptr: *mut c_char) {
509    if !ptr.is_null() {
510        unsafe {
511            let _ = CString::from_raw(ptr);
512        }
513    }
514}
515/// Get version information
516#[cfg(feature = "php")]
517#[no_mangle]
518pub extern "C" fn helix_version() -> *mut c_char {
519    let version = env!("CARGO_PKG_VERSION");
520    match CString::new(version) {
521        Ok(cstring) => cstring.into_raw(),
522        Err(_) => std::ptr::null_mut(),
523    }
524}
525/// Test function to verify FFI is working
526#[cfg(feature = "php")]
527#[no_mangle]
528pub extern "C" fn helix_test_ffi() -> *mut c_char {
529    match CString::new("Hello from Helix PHP SDK FFI!") {
530        Ok(cstring) => cstring.into_raw(),
531        Err(_) => std::ptr::null_mut(),
532    }
533}
534/// Initialize the PHP SDK
535#[cfg(feature = "php")]
536#[no_mangle]
537pub extern "C" fn helix_init() {}
538#[cfg(feature = "js")]
539/// JavaScript wrapper for Helix values
540#[napi(js_name = "Value")]
541#[derive(Clone)]
542pub struct JsValue {
543    inner: HlxValue,
544}
545#[cfg(feature = "js")]
546#[napi]
547impl JsValue {
548    #[napi(constructor)]
549    pub fn new(value: String) -> Result<Self> {
550        Ok(JsValue {
551            inner: HlxValue::String(value),
552        })
553    }
554    #[napi(getter)]
555    pub fn type_name(&self) -> &str {
556        match &self.inner {
557            HlxValue::String(_) => "string",
558            HlxValue::Number(_) => "number",
559            HlxValue::Bool(_) => "boolean",
560            HlxValue::Array(_) => "array",
561            HlxValue::Object(_) => "object",
562            HlxValue::Null => "null",
563        }
564    }
565    #[napi(getter)]
566    pub fn is_string(&self) -> bool {
567        matches!(& self.inner, HlxValue::String(_))
568    }
569    #[napi(getter)]
570    pub fn is_number(&self) -> bool {
571        matches!(& self.inner, HlxValue::Number(_))
572    }
573    #[napi(getter)]
574    pub fn is_boolean(&self) -> bool {
575        matches!(& self.inner, HlxValue::Bool(_))
576    }
577    #[napi(getter)]
578    pub fn is_array(&self) -> bool {
579        matches!(& self.inner, HlxValue::Array(_))
580    }
581    #[napi(getter)]
582    pub fn is_object(&self) -> bool {
583        matches!(& self.inner, HlxValue::Object(_))
584    }
585    #[napi(getter)]
586    pub fn is_null(&self) -> bool {
587        matches!(& self.inner, HlxValue::Null)
588    }
589    #[napi]
590    pub fn as_string(&self) -> Option<String> {
591        match &self.inner {
592            HlxValue::String(s) => Some(s.clone()),
593            _ => None,
594        }
595    }
596    #[napi]
597    pub fn as_number(&self) -> Option<f64> {
598        match &self.inner {
599            HlxValue::Number(n) => Some(*n),
600            _ => None,
601        }
602    }
603    #[napi]
604    pub fn as_boolean(&self) -> Option<bool> {
605        match &self.inner {
606            HlxValue::Bool(b) => Some(*b),
607            _ => None,
608        }
609    }
610    #[napi]
611    pub fn as_array(&self) -> Option<Vec<JsValue>> {
612        match &self.inner {
613            HlxValue::Array(arr) => {
614                Some(arr.iter().map(|v| JsValue { inner: v.clone() }).collect())
615            }
616            _ => None,
617        }
618    }
619    #[napi]
620    pub fn as_object(&self) -> Option<HashMap<String, JsValue>> {
621        match &self.inner {
622            HlxValue::Object(obj) => {
623                let mut result = HashMap::new();
624                for (k, v) in obj {
625                    result.insert(k.clone(), JsValue { inner: v.clone() });
626                }
627                Some(result)
628            }
629            _ => None,
630        }
631    }
632    #[napi]
633    pub fn to_string(&self) -> String {
634        match &self.inner {
635            HlxValue::String(s) => s.clone(),
636            HlxValue::Number(n) => n.to_string(),
637            HlxValue::Bool(b) => b.to_string(),
638            HlxValue::Array(arr) => format!("[{}]", arr.len()),
639            HlxValue::Object(obj) => format!("{{{}}}", obj.len()),
640            HlxValue::Null => "null".to_string(),
641        }
642    }
643    #[napi]
644    pub fn to_json(&self) -> String {
645        serde_json::to_string(&self.inner).unwrap_or_else(|_| "null".to_string())
646    }
647}
648#[cfg(feature = "js")]
649/// JavaScript wrapper for HelixConfig
650#[napi(js_name = "HelixConfig")]
651pub struct JsHelixConfig {
652    config: HelixConfig,
653}
654#[cfg(feature = "js")]
655#[napi]
656impl JsHelixConfig {
657    #[napi(constructor)]
658    pub fn new() -> Self {
659        JsHelixConfig {
660            config: HelixConfig::default(),
661        }
662    }
663    #[napi]
664    pub fn get(&self, key: String) -> Option<JsValue> {
665        Some(JsValue {
666            inner: HlxValue::String(key),
667        })
668    }
669    #[napi]
670    pub fn set(&mut self, key: String, value: String) -> Result<()> {
671        Ok(())
672    }
673    #[napi]
674    pub fn keys(&self) -> Vec<String> {
675        let mut keys = Vec::new();
676        if !self.config.agents.is_empty() {
677            keys.push("agents".to_string());
678        }
679        if !self.config.workflows.is_empty() {
680            keys.push("workflows".to_string());
681        }
682        if !self.config.contexts.is_empty() {
683            keys.push("contexts".to_string());
684        }
685        if self.config.memory.is_some() {
686            keys.push("memory".to_string());
687        }
688        keys
689    }
690    #[napi]
691    pub fn has(&self, key: String) -> bool {
692        match key.as_str() {
693            "agents" => !self.config.agents.is_empty(),
694            "workflows" => !self.config.workflows.is_empty(),
695            "contexts" => !self.config.contexts.is_empty(),
696            "memory" => self.config.memory.is_some(),
697            "crews" => !self.config.crews.is_empty(),
698            _ => false,
699        }
700    }
701    #[napi]
702    pub fn size(&self) -> u32 {
703        self.keys().len() as u32
704    }
705    #[napi]
706    pub fn items(&self) -> HashMap<String, String> {
707        let mut result = HashMap::new();
708        for key in self.keys() {
709            let value = self
710                .get(key.clone())
711                .map(|v| v.to_string())
712                .unwrap_or_else(|| "null".to_string());
713            result.insert(key, value);
714        }
715        result
716    }
717    #[napi]
718    pub fn delete(&mut self, key: String) -> bool {
719        false
720    }
721    #[napi]
722    pub fn clear(&mut self) -> Result<()> {
723        self.config = HelixConfig::default();
724        Ok(())
725    }
726    #[napi]
727    pub fn to_object(&self) -> HashMap<String, JsValue> {
728        let mut result = HashMap::new();
729        result
730            .insert(
731                "agents_count".to_string(),
732                JsValue {
733                    inner: HlxValue::Number(self.config.agents.len() as f64),
734                },
735            );
736        result
737            .insert(
738                "workflows_count".to_string(),
739                JsValue {
740                    inner: HlxValue::Number(self.config.workflows.len() as f64),
741                },
742            );
743        result
744            .insert(
745                "contexts_count".to_string(),
746                JsValue {
747                    inner: HlxValue::Number(self.config.contexts.len() as f64),
748                },
749            );
750        result
751    }
752}
753#[cfg(feature = "js")]
754#[napi]
755pub fn parse_helix_config(source: String) -> Result<JsHelixConfig> {
756    match crate::parse_and_validate(&source) {
757        Ok(config) => Ok(JsHelixConfig { config }),
758        Err(err) => Err(Error::from_reason(format!("Parse error: {}", err))),
759    }
760}
761#[cfg(feature = "js")]
762#[napi]
763pub async fn execute(
764    expression: String,
765    context: Option<HashMap<String, String>>,
766) -> Result<JsValue> {
767    let rt = tokio::runtime::Runtime::new()
768        .map_err(|e| Error::from_reason(format!("Runtime error: {}", e)))?;
769    let result = rt
770        .block_on(async {
771            let mut interpreter = crate::interpreter::HelixInterpreter::new()
772                .await
773                .map_err(|e| Error::from_reason(
774                    format!("Interpreter initialization error: {}", e),
775                ))?;
776            match crate::parse(&expression) {
777                Ok(ast) => {
778                    match interpreter.execute_ast(&ast).await {
779                        Ok(value) => Ok(JsValue { inner: value }),
780                        Err(e) => {
781                            Err(Error::from_reason(format!("Execution error: {}", e)))
782                        }
783                    }
784                }
785                Err(parse_err) => {
786                    let result_value = HlxValue::String(
787                        format!("Expression result: {}", expression),
788                    );
789                    Ok(JsValue { inner: result_value })
790                }
791            }
792        });
793    result
794}
795#[cfg(feature = "js")]
796#[napi]
797pub fn load_helix_config(file_path: String) -> Result<JsHelixConfig> {
798    match crate::load_file(&file_path) {
799        Ok(config) => Ok(JsHelixConfig { config }),
800        Err(err) => Err(napi::Error::from_reason(format!("File load error: {}", err))),
801    }
802}
803#[cfg(feature = "js")]
804#[napi(js_name = "ExecutionContext")]
805#[derive(Clone)]
806pub struct JsExecutionContext {
807    request: Option<HashMap<String, String>>,
808    session: HashMap<String, String>,
809    cookies: HashMap<String, String>,
810    params: HashMap<String, String>,
811    query: HashMap<String, String>,
812}
813#[cfg(feature = "js")]
814#[napi]
815impl JsExecutionContext {
816    #[napi(constructor)]
817    pub fn new(
818        request: Option<HashMap<String, String>>,
819        session: Option<HashMap<String, String>>,
820        cookies: Option<HashMap<String, String>>,
821        params: Option<HashMap<String, String>>,
822        query: Option<HashMap<String, String>>,
823    ) -> Self {
824        JsExecutionContext {
825            request,
826            session: session.unwrap_or_default(),
827            cookies: cookies.unwrap_or_default(),
828            params: params.unwrap_or_default(),
829            query: query.unwrap_or_default(),
830        }
831    }
832    #[napi(getter)]
833    pub fn request(&self) -> Option<HashMap<String, String>> {
834        self.request.clone()
835    }
836    #[napi(getter)]
837    pub fn session(&self) -> HashMap<String, String> {
838        self.session.clone()
839    }
840    #[napi(getter)]
841    pub fn cookies(&self) -> HashMap<String, String> {
842        self.cookies.clone()
843    }
844    #[napi(getter)]
845    pub fn params(&self) -> HashMap<String, String> {
846        self.params.clone()
847    }
848    #[napi(getter)]
849    pub fn query(&self) -> HashMap<String, String> {
850        self.query.clone()
851    }
852}
853#[cfg(feature = "js")]
854#[napi(js_name = "OperatorRegistry")]
855pub struct JsOperatorRegistry {
856    context: Option<JsExecutionContext>,
857}
858#[cfg(feature = "js")]
859#[napi]
860impl JsOperatorRegistry {
861    #[napi(constructor)]
862    pub fn new(context: Option<&JsExecutionContext>) -> Self {
863        JsOperatorRegistry {
864            context: context.map(|c| c.clone()),
865        }
866    }
867    #[napi(getter)]
868    pub fn context(&self) -> Option<JsExecutionContext> {
869        self.context.clone()
870    }
871    #[napi]
872    pub async fn execute(&self, operator: String, params: String) -> Result<JsValue> {
873        let result = HlxValue::String(
874            format!("Operator '{}' executed with params: {}", operator, params),
875        );
876        Ok(JsValue { inner: result })
877    }
878}
879#[cfg(feature = "js")]
880#[napi(js_name = "HelixInterpreter")]
881pub struct JsHelixInterpreter {
882    interpreter: Option<crate::interpreter::HelixInterpreter>,
883}
884#[cfg(feature = "js")]
885#[napi]
886impl JsHelixInterpreter {
887    #[napi(constructor)]
888    pub fn new() -> Result<Self> {
889        Ok(JsHelixInterpreter {
890            interpreter: None,
891        })
892    }
893    #[napi]
894    pub async unsafe fn execute(&mut self, expression: String) -> Result<JsValue> {
895        if self.interpreter.is_none() {
896            let interpreter = crate::interpreter::HelixInterpreter::new()
897                .await
898                .map_err(|e| Error::from_reason(
899                    format!("Interpreter initialization error: {}", e),
900                ))?;
901            self.interpreter = Some(interpreter);
902        }
903        let interpreter = self.interpreter.as_mut().unwrap();
904        match crate::parse(&expression) {
905            Ok(ast) => {
906                match interpreter.execute_ast(&ast).await {
907                    Ok(value) => Ok(JsValue { inner: value }),
908                    Err(e) => Err(Error::from_reason(format!("Execution error: {}", e))),
909                }
910            }
911            Err(parse_err) => {
912                let result_value = HlxValue::String(
913                    format!("Expression result: {}", expression),
914                );
915                Ok(JsValue { inner: result_value })
916            }
917        }
918    }
919    #[napi]
920    pub async unsafe fn set_variable(
921        &mut self,
922        name: String,
923        value: String,
924    ) -> Result<()> {
925        if self.interpreter.is_none() {
926            let interpreter = crate::interpreter::HelixInterpreter::new()
927                .await
928                .map_err(|e| Error::from_reason(
929                    format!("Interpreter initialization error: {}", e),
930                ))?;
931            self.interpreter = Some(interpreter);
932        }
933        if let Some(interpreter) = &mut self.interpreter {
934            interpreter.set_variable(name, HlxValue::String(value));
935        }
936        Ok(())
937    }
938    #[napi]
939    pub fn get_variable(&self, name: String) -> Option<JsValue> {
940        self.interpreter
941            .as_ref()?
942            .get_variable(&name)
943            .map(|v| JsValue { inner: v.clone() })
944    }
945}