Skip to content

Commit 658afbc

Browse files
committed
feat(ovsm): add NOW tool and improve error reporting
- Add NOW() tool to OVSM stdlib for Unix timestamp - Improve OVSM execution error messages to show actual errors - Fix RPC bridge to handle Solana parameter format (WIP) - Add debug logging for RPC calls Issue: RPC bridge still not correctly building config objects from named parameters. Currently sends address and raw values instead of address + config object. Example: sends ['addr', 5] instead of ['addr', {limit: 5}]
1 parent 678fb76 commit 658afbc

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

crates/ovsm/src/tools/stdlib/utilities.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::tools::{Tool, ToolRegistry};
88
pub fn register(registry: &mut ToolRegistry) {
99
registry.register(LogTool);
1010
registry.register(ErrorTool);
11+
registry.register(NowTool);
1112
}
1213

1314
/// Tool for logging messages to stdout (debugging purposes)
@@ -60,3 +61,31 @@ impl Tool for ErrorTool {
6061
Err(Error::UserError(message))
6162
}
6263
}
64+
65+
/// Tool for getting current Unix timestamp
66+
///
67+
/// Usage: `NOW()` - returns current Unix timestamp in seconds
68+
/// Example: `$timestamp = NOW()` returns 1697123456
69+
/// Note: Returns integer value representing seconds since Unix epoch
70+
pub struct NowTool;
71+
72+
impl Tool for NowTool {
73+
fn name(&self) -> &str {
74+
"NOW"
75+
}
76+
77+
fn description(&self) -> &str {
78+
"Get current Unix timestamp in seconds"
79+
}
80+
81+
fn execute(&self, _args: &[Value]) -> Result<Value> {
82+
use std::time::{SystemTime, UNIX_EPOCH};
83+
let now = SystemTime::now()
84+
.duration_since(UNIX_EPOCH)
85+
.map_err(|e| Error::ToolExecutionError {
86+
tool: "NOW".to_string(),
87+
reason: format!("Failed to get system time: {}", e),
88+
})?;
89+
Ok(Value::Int(now.as_secs() as i64))
90+
}
91+
}

src/services/ovsm_service.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ impl OvsmService {
9090
let result = self
9191
.evaluator
9292
.execute(&program)
93-
.context("Failed to execute OVSM program. Note: OVSM supports both Python-style (colon + indentation) and C-style (braces) syntax for control flow.")?;
93+
.map_err(|e| anyhow::anyhow!("Execution error: {}.\n\nNote: OVSM supports both Python-style (colon + indentation) and C-style (braces) syntax for control flow.", e))?;
9494

9595
if self.verbose {
9696
println!("✨ Execution completed successfully");

src/utils/rpc_bridge.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,40 @@ impl Tool for RpcBridgeTool {
3434
fn execute(&self, args: &[OvsmValue]) -> OvsmResult<OvsmValue> {
3535
let mut rpc_params = Vec::new();
3636

37+
// Handle single object argument (named parameters)
3738
if args.len() == 1 {
3839
if let OvsmValue::Object(obj) = &args[0] {
39-
if let Some(address) = obj.get("address") {
40-
rpc_params.push(ovsm_value_to_json(address));
40+
// Extract primary parameter (address, signature, etc.)
41+
let primary_param = obj.get("address")
42+
.or_else(|| obj.get("signature"))
43+
.or_else(|| obj.get("slot"))
44+
.or_else(|| obj.get("pubkey"));
4145

42-
let mut options = serde_json::Map::new();
46+
if let Some(primary) = primary_param {
47+
rpc_params.push(ovsm_value_to_json(primary));
48+
49+
// Build config object from remaining named parameters
50+
let mut config = serde_json::Map::new();
4351
for (key, val) in obj.iter() {
44-
if key != "address" {
45-
options.insert(key.clone(), ovsm_value_to_json(val));
52+
if !matches!(key.as_str(), "address" | "signature" | "slot" | "pubkey") {
53+
config.insert(key.clone(), ovsm_value_to_json(val));
4654
}
4755
}
48-
if !options.is_empty() {
49-
rpc_params.push(Value::Object(options));
56+
57+
// Only add config object if it has fields
58+
if !config.is_empty() {
59+
rpc_params.push(Value::Object(config));
5060
}
5161
} else {
62+
// No primary param found, use the whole object
5263
rpc_params.push(ovsm_value_to_json(&args[0]));
5364
}
5465
} else {
66+
// Single non-object argument
5567
rpc_params.push(ovsm_value_to_json(&args[0]));
5668
}
5769
} else {
70+
// Multiple arguments - use as positional
5871
for arg in args {
5972
rpc_params.push(ovsm_value_to_json(arg));
6073
}
@@ -85,6 +98,9 @@ async fn call_solana_rpc(method: &str, params: Vec<Value>) -> anyhow::Result<Val
8598
"params": params
8699
});
87100

101+
// Debug: print RPC request
102+
eprintln!("[RPC DEBUG] Method: {}, Params: {}", method, serde_json::to_string_pretty(&params).unwrap_or_else(|_| "invalid".to_string()));
103+
88104
let response = client
89105
.post(SOLANA_RPC_URL)
90106
.json(&request_body)

0 commit comments

Comments
 (0)