use futures::stream::StreamExt;
use iflow_cli_sdk_rust::{IFlowClient, IFlowOptions, Message};
use iflow_cli_sdk_rust::error::IFlowError;
use std::io::Write;
use std::process::Command;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
println!("๐ Starting iFlow WebSocket client example for existing process...");
println!("๐ง Starting iFlow process manually...");
let mut iflow_process = Command::new("iflow")
.arg("--experimental-acp")
.arg("--port")
.arg("8093")
.spawn()
.expect("Failed to start iFlow process");
println!("โณ Waiting for iFlow process to be ready...");
let mut attempts = 0;
let max_attempts = 30;
while attempts < max_attempts {
if std::net::TcpStream::connect_timeout(
&"127.0.0.1:8093".parse().unwrap(),
std::time::Duration::from_millis(100),
)
.is_ok()
{
println!("โ
iFlow WebSocket server is ready on port 8093");
break;
}
attempts += 1;
if attempts % 5 == 0 {
println!(
"โณ Still waiting for iFlow to be ready... (attempt {}/{})",
attempts, max_attempts
);
}
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
if attempts >= max_attempts {
eprintln!(
"โ iFlow process failed to start WebSocket server on port 8093 after {} seconds",
max_attempts
);
let _ = iflow_process.kill();
let _ = iflow_process.wait();
return Err("iFlow process failed to start".into());
}
let local = tokio::task::LocalSet::new();
local
.run_until(async {
let custom_timeout_secs = 120.0;
let options = IFlowOptions::new()
.with_websocket_config(iflow_cli_sdk_rust::types::WebSocketConfig::new(
"ws://localhost:8093/acp?peer=iflow".to_string(),
))
.with_timeout(custom_timeout_secs);
let mut client = IFlowClient::new(Some(options));
println!("๐ Connecting to existing iFlow process via WebSocket...");
client.connect().await?;
println!("โ
Connected to existing iFlow process via WebSocket");
println!("๐ฅ Receiving responses...");
let mut message_stream = client.messages();
let message_task = tokio::task::spawn_local(async move {
let mut stdout = std::io::stdout();
while let Some(message) = message_stream.next().await {
match message {
Message::Assistant { content } => {
print!("{}", content);
stdout
.flush()
.map_err(|err| -> Box<dyn std::error::Error> { Box::new(err) })?;
}
Message::ToolCall { id, name, status } => {
println!("\n๐ง Tool call: {} ({}) {}", id, name, status);
}
Message::Plan { entries } => {
println!("\n๐ Plan update received: {:?}", entries);
}
Message::TaskFinish { .. } => {
println!("\nโ
Task completed");
break;
}
Message::Error {
code,
message: msg,
details: _,
} => {
eprintln!("\nโ Error {}: {}", code, msg);
break;
}
Message::User { content } => {
println!("\n๐ค User message: {}", content);
}
}
}
Ok::<(), Box<dyn std::error::Error>>(())
});
let prompt = "Create a plan to introduce this project.";
println!("๐ค Sending: {}", prompt);
match client.send_message(prompt, None).await {
Ok(()) => {
println!("โ
Message sent successfully");
}
Err(IFlowError::Timeout(msg)) => {
eprintln!("โฐ Timeout error occurred: {}", msg);
eprintln!("This may be due to processing delays.");
eprintln!("Consider increasing the timeout or checking the iFlow process.");
}
Err(e) => {
eprintln!("โ Error sending message: {}", e);
return Err(e.into());
}
}
match tokio::time::timeout(
std::time::Duration::from_secs_f64(custom_timeout_secs),
message_task,
)
.await
{
Ok(Ok(Ok(()))) => {
println!("โ
Message handling completed successfully");
}
Ok(Ok(Err(err))) => {
eprintln!("โ Error in message handling: {}", err);
}
Ok(Err(err)) => {
eprintln!("โ Message task panicked: {}", err);
}
Err(_) => {
println!("โฐ Timeout waiting for message handling to complete");
}
}
println!("\n๐ Disconnecting...");
client.disconnect().await?;
println!("๐ Disconnected from iFlow");
Ok::<(), Box<dyn std::error::Error>>(())
})
.await?;
println!("๐งน Cleaning up iFlow process...");
let _ = iflow_process.kill();
let _ = iflow_process.wait();
Ok(())
}