diff --git a/Cargo.lock b/Cargo.lock index d74e351..3e8fdc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3825,6 +3825,7 @@ dependencies = [ "strum", "strum_macros", "thiserror 2.0.12", + "toml", ] [[package]] @@ -4045,9 +4046,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -4057,26 +4058,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tower" version = "0.5.2" @@ -4812,9 +4820,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b" dependencies = [ "memchr", ] diff --git a/hermes/src/main.rs b/hermes/src/main.rs index b798f3b..028cca8 100644 --- a/hermes/src/main.rs +++ b/hermes/src/main.rs @@ -113,7 +113,7 @@ async fn eval(state: Arc>) { AgentInstructionBody::Script(script) => { task::spawn_blocking(async move || { let mut engine = ScriptingEngine::new(); - engine.execute(&script.source).await; + engine.execute(&script.body).await; }) .await; diff --git a/talaria/Cargo.toml b/talaria/Cargo.toml index 549fed0..473e3a7 100644 --- a/talaria/Cargo.toml +++ b/talaria/Cargo.toml @@ -16,3 +16,4 @@ strum = "0.27" strum_macros = "0.27" bytesize = "2.0.1" rhai = { version = "1.21", featues = ["sync"] } +toml = "0.8.22" diff --git a/talaria/src/console.rs b/talaria/src/console.rs index 7c3240d..0129fb3 100644 --- a/talaria/src/console.rs +++ b/talaria/src/console.rs @@ -303,6 +303,15 @@ pub enum GroupCommand { None, } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, EnumProperty, EnumIter)] +pub enum Param { + String(String), + Int(i64), + Float(f64), + Bool(bool), + Array(Vec), +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, EnumProperty, Default, EnumIter)] pub enum RunCommand { #[strum(props( @@ -314,6 +323,7 @@ pub enum RunCommand { Script { target: Option, script_name: String, + params: Vec, }, #[strum(props( @@ -391,13 +401,12 @@ pub enum CommandError { ExpectedRhai, #[error("expected shell script")] ExpectedShell, -} - -pub enum Token { - CommandName { command_name: String }, - AgentID { id: u64 }, - AgentNickname { nickname: String }, - GroupIdentifier { identifier: String }, + #[error("expected script param")] + ExpectedScriptParam, + #[error("expected closing bracket")] + ExpectedClosingBracket, + #[error("invalid number")] + InvalidNumber, } pub struct Parser { @@ -477,6 +486,12 @@ impl Parser { tokens.push(current_token.iter().collect()); current_token.clear(); } + } else if char == '[' || char == ']' { + if current_token.len() == 0 { + tokens.push(char.to_string()); + } else { + current_token.push(char); + } } else { current_token.push(char); } @@ -617,7 +632,7 @@ impl Parser { ) -> Result, CommandError> { match last_arg { true => - // his is a little bit of a hack, + // this is a little bit of a hack, // but it's very ergonomic to use-- and i'm not sure how to // implement this in a nicer way // @@ -863,6 +878,7 @@ impl Parser { RunCommand::Script { .. } => RunCommand::Script { target: self.parse_opt_target_ident(false)?, script_name: self.parse_script_name()?, + params: self.parse_script_params()?, }, RunCommand::Rhai { .. } => RunCommand::Rhai { target: self.parse_opt_target_ident(false)?, @@ -885,6 +901,66 @@ impl Parser { _ => Err(CommandError::InvalidScriptName), } } + + pub fn parse_script_params(&mut self) -> Result, CommandError> { + // FIXME: + Ok(vec![]) + // if self.is_at_end() { + // return Ok(vec![]); + // } + // + // let mut params = vec![]; + // + // loop { + // let token = self.peek(CommandError::ExpectedScriptParam); + // let token = token?; + // + // let next_char = token + // .chars() + // .next() + // .ok_or(CommandError::ExpectedClosingBracket)?; + // + // match next_char { + // 'a'..='z' | 'A'..='Z' => { + // if token == "true" { + // params.push(Param::Bool(true)); + // } + // + // if token == "false" { + // params.push(Param::Bool(false)); + // } + // + // params.push(self.parse_script_string()?); + // } + // '0'..'9' | '.' | '-' => params.push(self.parse_script_number()?), + // '[' => {} + // ']' => {} + // _ => {} + // } + // } + } + + pub fn parse_script_number(&mut self) -> Result { + let token = self.consume(CommandError::ExpectedScriptParam)?; + + match token.parse::() { + Ok(float) => return Ok(Param::Float(float)), + Err(_) => {} + } + + match token.parse::() { + Ok(int) => return Ok(Param::Int(int)), + Err(_) => {} + } + + return Err(CommandError::InvalidNumber); + } + + pub fn parse_script_string(&mut self) -> Result { + let token = self.consume(CommandError::ExpectedScriptParam); + + Ok(Param::String(token?.to_string())) + } } pub struct Console { diff --git a/talaria/src/protocol.rs b/talaria/src/protocol.rs index c42412d..6e82173 100644 --- a/talaria/src/protocol.rs +++ b/talaria/src/protocol.rs @@ -1,23 +1,9 @@ +use crate::scripting::Script; + use anyhow::Result; use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Encode, Decode, Serialize, Deserialize)] -pub struct Script { - pub source: String, - pub description: String, - pub title: String, -} - -impl ToString for Script { - fn to_string(&self) -> String { - format!( - "Title: {}\nDescription: {}\n\n{}", - self.title, self.description, self.source - ) - } -} - #[derive(Encode, Decode, Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct OS { pub os_type: OSType, @@ -136,7 +122,7 @@ impl AgentInstructionBody { format!("Command: {}\nArgs: {:#?}", command, args) } AgentInstructionBody::Ok => String::from("None"), - AgentInstructionBody::Script(script) => script.title.clone(), + AgentInstructionBody::Script(script) => script.name.clone(), AgentInstructionBody::Rhai(source) => source.clone(), AgentInstructionBody::Kill => String::from("None"), } diff --git a/talaria/src/scripting.rs b/talaria/src/scripting.rs index 0828a19..04760a6 100644 --- a/talaria/src/scripting.rs +++ b/talaria/src/scripting.rs @@ -1,5 +1,8 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; +use bincode::{Decode, Encode}; +use bytesize::Display; use rhai::{plugin::*, Scope}; +use serde::{Deserialize, Serialize}; use crate::stdlib::*; @@ -44,3 +47,119 @@ impl ScriptingEngine { &self.engine } } + +#[derive(Clone, Deserialize, Serialize, Encode, Decode, Debug)] +pub struct Script { + pub name: String, + pub description: String, + pub params: Vec, + #[serde(skip)] + pub body: String, +} + +impl Script { + pub fn signature(&self) -> String { + let parameters = self + .params + .clone() + .into_iter() + .map(|p| format!("[{}: {}]", p.name, p.r#type.to_string())) + .collect::>() + .join(" "); + + let example = format!( + "{} {}", + self.name, + self.params + .clone() + .into_iter() + .map(|p| format!("{}", p.placeholder)) + .collect::>() + .join(" ") + ); + + format!( + "Name: {}\nDescription: {}\nParameters: {}\nExample: {}", + self.name, self.description, parameters, example + ) + } + + pub fn from_str(input: &str) -> Result