chore: monorepo inicial con arje + minga + yahweh absorbidos
Workspace en 4 ejes (core/modules/apps/shared):
- core/: 24 crates de arje (Init systemd-compatible: ente-card, ente-zero,
ente-kernel, ente-bus, ente-cas, ente-soma, ente-wasm, ente-snapshot,
ente-brain, ente-echo, ente-policy-provider, + 12 crates *-compat)
- modules/semantic_dht/: 5 crates de minga (minga-core con AST/CAS/MST,
minga-p2p con libp2p Kad, minga-store, minga-vfs, minga-cli)
- modules/ui_engine/: 11 crates de yahweh (libs/{core,theme,bus,providers},
widgets/{tree,splitter,tabs,tiled,container_core,text_input})
- apps/: 5 crates de yahweh (file_explorer, database_explorer, text_viewer,
image_viewer, yahweh-shell)
- shared_wit/protocol.wit: handshake/lifecycle inicial
Cargo.toml unificado: thiserror bumped a 2 (transparente para arje), tokio
"full", paths intra-workspace de yahweh redirigidos a su nueva ubicación.
cargo check --workspace: 0 errores, 17 warnings (dead code preexistente).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
//! Binario `minga`: argument parsing y formateo de salida.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use minga_cli::{
|
||||
cmd_ingest, cmd_init, cmd_listen, cmd_status, cmd_sync, cmd_watch, CliError,
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
name = "minga",
|
||||
version,
|
||||
about = "Minga: VCS semántico P2P. Versiona AST, no líneas."
|
||||
)]
|
||||
struct Cli {
|
||||
/// Ruta del repositorio Minga. Por defecto: `.minga` en el cwd.
|
||||
#[arg(short, long, default_value = ".minga", global = true)]
|
||||
repo: PathBuf,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
/// Inicializa un nuevo repo: genera keypair Ed25519, lo cifra con
|
||||
/// passphrase, y crea el almacén persistente vacío.
|
||||
Init,
|
||||
|
||||
/// Muestra DID, tamaño del MST, nodos almacenados y atestaciones.
|
||||
Status,
|
||||
|
||||
/// Parsea un archivo Rust, lo añade al MST y firma una atestación
|
||||
/// de autoría con la identidad del repo.
|
||||
Ingest {
|
||||
/// Ruta del archivo .rs a ingerir.
|
||||
file: PathBuf,
|
||||
},
|
||||
|
||||
/// Escucha conexiones de peers en una multiaddr libp2p y acepta
|
||||
/// sincronizaciones entrantes hasta Ctrl+C.
|
||||
Listen {
|
||||
/// Multiaddr libp2p, ej. `/ip4/0.0.0.0/tcp/4001`.
|
||||
addr: String,
|
||||
},
|
||||
|
||||
/// Sincroniza una vez con un peer remoto (multiaddr con `/p2p/<id>`).
|
||||
Sync {
|
||||
/// Multiaddr completo, ej. `/ip4/1.2.3.4/tcp/4001/p2p/12D3KooW...`.
|
||||
peer: String,
|
||||
},
|
||||
|
||||
/// Vigila un directorio y re-ingiere automáticamente cualquier
|
||||
/// archivo `.rs` que se cree o modifique. Minga como VCS de fondo:
|
||||
/// el usuario escribe en su editor y el código queda versionado.
|
||||
Watch {
|
||||
/// Directorio a vigilar.
|
||||
dir: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
eprintln!("error: {}", e);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> Result<(), CliError> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
match cli.command {
|
||||
Command::Init => {
|
||||
let pass = prompt_passphrase_with_confirm()?;
|
||||
let did = cmd_init(&cli.repo, &pass)?;
|
||||
println!("Repo inicializado en {}", cli.repo.display());
|
||||
println!("DID: {}", did);
|
||||
}
|
||||
Command::Status => {
|
||||
let pass = prompt_passphrase()?;
|
||||
let s = cmd_status(&cli.repo, &pass)?;
|
||||
println!("DID: {}", s.did);
|
||||
println!("MST: {} claves", s.mst_len);
|
||||
println!("Nodos almacenados: {}", s.nodes_len);
|
||||
println!("Atestaciones: {}", s.attestations_len);
|
||||
}
|
||||
Command::Ingest { file } => {
|
||||
let pass = prompt_passphrase()?;
|
||||
let r = cmd_ingest(&cli.repo, &pass, &file)?;
|
||||
println!("Ingerido: {}", file.display());
|
||||
println!("Hash: {}", r.hash);
|
||||
println!("Firmado por: {}", r.did);
|
||||
}
|
||||
Command::Listen { addr } => {
|
||||
let pass = prompt_passphrase()?;
|
||||
let rt = tokio::runtime::Runtime::new()
|
||||
.map_err(|e| CliError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
|
||||
rt.block_on(cmd_listen(&cli.repo, &pass, &addr))?;
|
||||
}
|
||||
Command::Sync { peer } => {
|
||||
let pass = prompt_passphrase()?;
|
||||
let rt = tokio::runtime::Runtime::new()
|
||||
.map_err(|e| CliError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
|
||||
rt.block_on(cmd_sync(&cli.repo, &pass, &peer))?;
|
||||
println!("Sync completo.");
|
||||
}
|
||||
Command::Watch { dir } => {
|
||||
let pass = prompt_passphrase()?;
|
||||
println!("Vigilando {}. Ctrl+C para parar.", dir.display());
|
||||
let rt = tokio::runtime::Runtime::new()
|
||||
.map_err(|e| CliError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
|
||||
rt.block_on(cmd_watch(&cli.repo, &pass, &dir))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prompt_passphrase() -> Result<String, CliError> {
|
||||
let pass = rpassword::prompt_password("Passphrase: ")
|
||||
.map_err(CliError::Io)?;
|
||||
Ok(pass)
|
||||
}
|
||||
|
||||
fn prompt_passphrase_with_confirm() -> Result<String, CliError> {
|
||||
let pass = rpassword::prompt_password("Passphrase nueva: ")
|
||||
.map_err(CliError::Io)?;
|
||||
let conf = rpassword::prompt_password("Confirma: ")
|
||||
.map_err(CliError::Io)?;
|
||||
if pass != conf {
|
||||
return Err(CliError::Io(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"passphrases no coinciden",
|
||||
)));
|
||||
}
|
||||
Ok(pass)
|
||||
}
|
||||
Reference in New Issue
Block a user