1use clap::{Parser, Subcommand};
7use anyhow::Result;
8use serde::{Serialize, Deserialize};
9use synaptic_qudag_core::QuDAGNetwork;
10use synaptic_neural_wasm::{NeuralNetwork, Layer};
11use synaptic_neural_mesh::{NeuralMesh, Agent};
12use synaptic_daa_swarm::{Swarm, SwarmBehavior};
13use claude_market::{ClaudeMarket, MarketConfig};
14
15#[derive(Parser, Debug)]
17#[command(author, version, about, long_about = None)]
18pub struct Cli {
19 #[command(subcommand)]
20 pub command: Commands,
21}
22
23#[derive(Subcommand, Debug)]
25pub enum Commands {
26 Node {
28 #[command(subcommand)]
29 action: NodeAction,
30 },
31 Swarm {
33 #[command(subcommand)]
34 action: SwarmAction,
35 },
36 Neural {
38 #[command(subcommand)]
39 action: NeuralAction,
40 },
41 Mesh {
43 #[command(subcommand)]
44 action: MeshAction,
45 },
46 Market {
48 #[command(subcommand)]
49 action: MarketAction,
50 },
51 Wallet {
53 #[command(subcommand)]
54 action: WalletAction,
55 },
56 Status,
58}
59
60#[derive(Subcommand, Debug)]
62pub enum NodeAction {
63 Start {
65 #[arg(short, long, default_value = "8080")]
66 port: u16,
67 },
68 Stop,
70 List,
72}
73
74#[derive(Subcommand, Debug)]
76pub enum SwarmAction {
77 Create {
79 #[arg(short, long, default_value = "10")]
80 agents: usize,
81 #[arg(short, long)]
82 behavior: Option<String>,
83 },
84 Run {
86 #[arg(short, long)]
87 id: Option<String>,
88 },
89 List,
91}
92
93#[derive(Subcommand, Debug)]
95pub enum NeuralAction {
96 Create {
98 #[arg(short, long)]
99 layers: Vec<usize>,
100 #[arg(short, long)]
101 output: String,
102 },
103 Train {
105 #[arg(short, long)]
106 model: String,
107 #[arg(short, long)]
108 data: String,
109 },
110 Predict {
112 #[arg(short, long)]
113 model: String,
114 #[arg(short, long)]
115 input: Vec<f32>,
116 },
117}
118
119#[derive(Subcommand, Debug)]
121pub enum MeshAction {
122 Info,
124 AddAgent {
126 #[arg(short, long)]
127 name: String,
128 },
129 SubmitTask {
131 #[arg(short, long)]
132 name: String,
133 #[arg(short, long)]
134 compute: f64,
135 },
136}
137
138#[derive(Subcommand, Debug)]
140pub enum MarketAction {
141 Init {
143 #[arg(short, long)]
144 db_path: Option<String>,
145 },
146 Offer {
148 #[arg(short, long)]
149 slots: u64,
150 #[arg(short, long)]
151 price: u64,
152 #[arg(long)]
153 opt_in: bool,
154 },
155 Bid {
157 #[arg(short, long)]
158 task: String,
159 #[arg(short, long)]
160 max_price: u64,
161 },
162 Status {
164 #[arg(short, long)]
165 detailed: bool,
166 },
167 Terms,
169}
170
171#[derive(Subcommand, Debug)]
173pub enum WalletAction {
174 Balance,
176 Transfer {
178 #[arg(short, long)]
179 to: String,
180 #[arg(short, long)]
181 amount: u64,
182 #[arg(short, long)]
183 memo: Option<String>,
184 },
185 History {
187 #[arg(short, long, default_value = "10")]
188 limit: usize,
189 },
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub enum MeshCommand {
195 NodeStart { port: u16 },
196 NodeStop,
197 NodeList,
198 SwarmCreate { agents: usize, behavior: Option<SwarmBehavior> },
199 SwarmRun { id: Option<String> },
200 SwarmList,
201 NeuralCreate { layers: Vec<usize>, output: String },
202 NeuralTrain { model: String, data: String },
203 NeuralPredict { model: String, input: Vec<f32> },
204 MeshInfo,
205 MeshAddAgent { name: String },
206 MeshSubmitTask { name: String, compute: f64 },
207 MarketInit { db_path: Option<String> },
208 MarketOffer { slots: u64, price: u64, opt_in: bool },
209 MarketBid { task: String, max_price: u64 },
210 MarketStatus { detailed: bool },
211 MarketTerms,
212 WalletBalance,
213 WalletTransfer { to: String, amount: u64, memo: Option<String> },
214 WalletHistory { limit: usize },
215 Status,
216}
217
218pub async fn execute_command(command: MeshCommand) -> Result<CommandResult> {
220 match command {
221 MeshCommand::NodeStart { port } => {
222 let _network = QuDAGNetwork::new();
224 Ok(CommandResult::NodeStarted { port, id: "node-1".to_string() })
225 }
226
227 MeshCommand::NodeStop => {
228 Ok(CommandResult::NodeStopped)
229 }
230
231 MeshCommand::NodeList => {
232 Ok(CommandResult::NodeList { nodes: vec![] })
233 }
234
235 MeshCommand::SwarmCreate { agents, behavior } => {
236 let swarm = Swarm::new();
237 if let Some(b) = behavior {
238 swarm.add_behavior(b);
239 }
240 swarm.initialize(agents).await;
241 Ok(CommandResult::SwarmCreated { id: "swarm-1".to_string(), agents })
242 }
243
244 MeshCommand::SwarmRun { id } => {
245 let swarm = Swarm::new();
247 swarm.initialize(10).await;
248 Ok(CommandResult::SwarmRunning { id: id.unwrap_or("swarm-1".to_string()) })
250 }
251
252 MeshCommand::SwarmList => {
253 Ok(CommandResult::SwarmList { swarms: vec![] })
254 }
255
256 MeshCommand::NeuralCreate { layers, output } => {
257 let mut network = NeuralNetwork::new();
258
259 for i in 0..layers.len() - 1 {
261 let layer = Layer::dense(layers[i], layers[i + 1]);
262 network.add_layer(layer);
263 }
264
265 let json = network.to_json()?;
267 std::fs::write(&output, json)?;
268
269 Ok(CommandResult::NeuralCreated { path: output })
270 }
271
272 MeshCommand::NeuralTrain { model, data: _ } => {
273 let model_json = std::fs::read_to_string(&model)?;
275 let _network = NeuralNetwork::from_json(&model_json)?;
276
277 Ok(CommandResult::NeuralTrained { model, epochs: 100 })
279 }
280
281 MeshCommand::NeuralPredict { model, input } => {
282 let model_json = std::fs::read_to_string(&model)?;
283 let network = NeuralNetwork::from_json(&model_json)?;
284
285 let output_json = network.predict(&input)
287 .map_err(|e| anyhow::anyhow!("Prediction failed: {:?}", e))?;
288 let output: Vec<f32> = serde_json::from_str(&output_json)?;
289
290 Ok(CommandResult::NeuralPrediction { output })
291 }
292
293 MeshCommand::MeshInfo => {
294 let mesh = NeuralMesh::new();
295 let stats = mesh.get_stats();
296 Ok(CommandResult::MeshInfo {
297 agents: stats.total_agents,
298 tasks: stats.total_tasks,
299 })
300 }
301
302 MeshCommand::MeshAddAgent { name } => {
303 let mesh = NeuralMesh::new();
304 let agent = Agent::new(&name);
305 let id = mesh.add_agent(agent).await?;
306 Ok(CommandResult::AgentAdded { id: id.to_string(), name })
307 }
308
309 MeshCommand::MeshSubmitTask { name, compute } => {
310 let mesh = NeuralMesh::new();
311 let requirements = synaptic_neural_mesh::TaskRequirements {
312 min_compute_power: compute,
313 min_memory: 1024 * 1024,
314 required_specializations: vec!["general".to_string()],
315 max_latency_ms: 100.0,
316 };
317 let id = mesh.submit_task(&name, requirements).await?;
318 Ok(CommandResult::TaskSubmitted { id: id.to_string(), name })
319 }
320
321 MeshCommand::MarketInit { db_path } => {
322 let config = MarketConfig {
323 db_path: db_path.clone(),
324 ..Default::default()
325 };
326 let _market = ClaudeMarket::new(config).await?;
327 Ok(CommandResult::MarketInitialized {
328 db_path: db_path.unwrap_or("claude_market.db".to_string())
329 })
330 }
331
332 MeshCommand::MarketOffer { slots, price, opt_in } => {
333 if !opt_in {
334 return Err(anyhow::anyhow!("Market participation requires explicit opt-in with --opt-in flag"));
335 }
336 Ok(CommandResult::MarketOfferCreated { slots, price })
338 }
339
340 MeshCommand::MarketBid { task, max_price } => {
341 Ok(CommandResult::MarketBidSubmitted { task, max_price })
343 }
344
345 MeshCommand::MarketStatus { detailed: _ } => {
346 Ok(CommandResult::MarketStatus {
348 active_offers: 3,
349 active_bids: 7,
350 })
351 }
352
353 MeshCommand::MarketTerms => {
354 let terms = r#"
355SYNAPTIC MARKET TERMS OF SERVICE
356
357Synaptic Market facilitates peer compute federation, not API access resale.
358
359KEY COMPLIANCE REQUIREMENTS:
360✅ NO shared API keys - Each participant uses their own Claude subscription
361✅ LOCAL execution - Tasks run locally on provider's Claude account
362✅ VOLUNTARY participation - Full user control with opt-in mechanisms
363✅ TOKEN rewards - RUV tokens reward contribution, not access purchase
364
365LEGAL FRAMEWORK:
366• Each node maintains individual Claude subscriptions
367• Tasks are routed, not account access shared
368• Participation is voluntary and contribution-based
369• API keys are never shared or transmitted
370• This is peer compute federation, not resale
371
372By using Synaptic Market, you agree to maintain your own Claude subscription
373and comply with Anthropic's Terms of Service.
374"#;
375 Ok(CommandResult::MarketTerms { terms: terms.to_string() })
376 }
377
378 MeshCommand::WalletBalance => {
379 Ok(CommandResult::WalletBalance { balance: 1000 })
381 }
382
383 MeshCommand::WalletTransfer { to, amount, memo: _ } => {
384 Ok(CommandResult::WalletTransferCompleted { to, amount })
386 }
387
388 MeshCommand::WalletHistory { limit: _ } => {
389 Ok(CommandResult::WalletHistory {
391 transactions: vec![
392 "Transfer: 100 RUV to peer-123 (market_payment)".to_string(),
393 "Received: 50 RUV from peer-456 (task_completion)".to_string(),
394 ]
395 })
396 }
397
398 MeshCommand::Status => {
399 Ok(CommandResult::Status {
400 mesh_active: true,
401 nodes: 1,
402 agents: 0,
403 swarms: 0,
404 })
405 }
406 }
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
411pub enum CommandResult {
412 NodeStarted { port: u16, id: String },
413 NodeStopped,
414 NodeList { nodes: Vec<String> },
415 SwarmCreated { id: String, agents: usize },
416 SwarmRunning { id: String },
417 SwarmList { swarms: Vec<String> },
418 NeuralCreated { path: String },
419 NeuralTrained { model: String, epochs: usize },
420 NeuralPrediction { output: Vec<f32> },
421 MeshInfo { agents: usize, tasks: usize },
422 AgentAdded { id: String, name: String },
423 TaskSubmitted { id: String, name: String },
424 MarketInitialized { db_path: String },
425 MarketOfferCreated { slots: u64, price: u64 },
426 MarketBidSubmitted { task: String, max_price: u64 },
427 MarketStatus { active_offers: usize, active_bids: usize },
428 MarketTerms { terms: String },
429 WalletBalance { balance: u64 },
430 WalletTransferCompleted { to: String, amount: u64 },
431 WalletHistory { transactions: Vec<String> },
432 Status { mesh_active: bool, nodes: usize, agents: usize, swarms: usize },
433}
434
435pub fn cli_to_command(cli: Cli) -> MeshCommand {
437 match cli.command {
438 Commands::Node { action } => match action {
439 NodeAction::Start { port } => MeshCommand::NodeStart { port },
440 NodeAction::Stop => MeshCommand::NodeStop,
441 NodeAction::List => MeshCommand::NodeList,
442 },
443 Commands::Swarm { action } => match action {
444 SwarmAction::Create { agents, behavior } => {
445 let b = behavior.and_then(|s| match s.as_str() {
446 "flocking" => Some(SwarmBehavior::Flocking),
447 "foraging" => Some(SwarmBehavior::Foraging),
448 "exploration" => Some(SwarmBehavior::Exploration),
449 "consensus" => Some(SwarmBehavior::Consensus),
450 "optimization" => Some(SwarmBehavior::Optimization),
451 _ => None,
452 });
453 MeshCommand::SwarmCreate { agents, behavior: b }
454 },
455 SwarmAction::Run { id } => MeshCommand::SwarmRun { id },
456 SwarmAction::List => MeshCommand::SwarmList,
457 },
458 Commands::Neural { action } => match action {
459 NeuralAction::Create { layers, output } => MeshCommand::NeuralCreate { layers, output },
460 NeuralAction::Train { model, data } => MeshCommand::NeuralTrain { model, data },
461 NeuralAction::Predict { model, input } => MeshCommand::NeuralPredict { model, input },
462 },
463 Commands::Mesh { action } => match action {
464 MeshAction::Info => MeshCommand::MeshInfo,
465 MeshAction::AddAgent { name } => MeshCommand::MeshAddAgent { name },
466 MeshAction::SubmitTask { name, compute } => MeshCommand::MeshSubmitTask { name, compute },
467 },
468 Commands::Market { action } => match action {
469 MarketAction::Init { db_path } => MeshCommand::MarketInit { db_path },
470 MarketAction::Offer { slots, price, opt_in } => MeshCommand::MarketOffer { slots, price, opt_in },
471 MarketAction::Bid { task, max_price } => MeshCommand::MarketBid { task, max_price },
472 MarketAction::Status { detailed } => MeshCommand::MarketStatus { detailed },
473 MarketAction::Terms => MeshCommand::MarketTerms,
474 },
475 Commands::Wallet { action } => match action {
476 WalletAction::Balance => MeshCommand::WalletBalance,
477 WalletAction::Transfer { to, amount, memo } => MeshCommand::WalletTransfer { to, amount, memo },
478 WalletAction::History { limit } => MeshCommand::WalletHistory { limit },
479 },
480 Commands::Status => MeshCommand::Status,
481 }
482}
483
484pub fn init_tracing() {
486 tracing_subscriber::fmt()
487 .with_max_level(tracing::Level::DEBUG)
488 .init();
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 #[tokio::test]
496 async fn test_node_start() {
497 let cmd = MeshCommand::NodeStart { port: 8080 };
498 let result = execute_command(cmd).await.unwrap();
499
500 match result {
501 CommandResult::NodeStarted { port, .. } => assert_eq!(port, 8080),
502 _ => panic!("Unexpected result"),
503 }
504 }
505
506 #[tokio::test]
507 async fn test_swarm_create() {
508 let cmd = MeshCommand::SwarmCreate {
509 agents: 5,
510 behavior: Some(SwarmBehavior::Flocking)
511 };
512 let result = execute_command(cmd).await.unwrap();
513
514 match result {
515 CommandResult::SwarmCreated { agents, .. } => assert_eq!(agents, 5),
516 _ => panic!("Unexpected result"),
517 }
518 }
519
520 #[tokio::test]
521 async fn test_neural_create() {
522 let cmd = MeshCommand::NeuralCreate {
523 layers: vec![10, 5, 2],
524 output: "/tmp/test_model.json".to_string(),
525 };
526 let result = execute_command(cmd).await.unwrap();
527
528 match result {
529 CommandResult::NeuralCreated { path } => {
530 assert_eq!(path, "/tmp/test_model.json");
531 std::fs::remove_file(path).ok();
533 },
534 _ => panic!("Unexpected result"),
535 }
536 }
537}