diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index bf64f18..670489d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -61,7 +61,7 @@ platform-specific service integration (macOS / Linux / Windows). The wire-protocol Rust types are **generated from a JSON Schema** (via `schemars`/`typify`). The schema is the single source of truth so the daemon and its peer can be kept in lock-step; see -[`schema/tunnel-protocol.json`](./schema/tunnel-protocol.json). +[`schema/edison-tunnel-protocol.json`](./schema/edison-tunnel-protocol.json). ## Tunnel mechanism: reverse RPC over WebSocket @@ -85,7 +85,7 @@ reverse tunnel because: ### Wire protocol -Defined as JSON Schema at `schema/tunnel-protocol.json`. Frames are JSON with a +Defined as JSON Schema at `schema/edison-tunnel-protocol.json`. Frames are JSON with a `type` discriminator and fall into two categories. **Control frames** (lifecycle / desired state): diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2953edc..7406a8b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,7 +40,7 @@ cargo fmt --all --check && \ - **Match the surrounding style.** Follow the existing naming, comment density, and module layout; `cargo fmt` handles formatting. - **Update docs alongside code.** If you change the wire protocol, update - [`schema/tunnel-protocol.json`](./schema/tunnel-protocol.json) (the single + [`schema/edison-tunnel-protocol.json`](./schema/edison-tunnel-protocol.json) (the single source of truth) and [`ARCHITECTURE.md`](./ARCHITECTURE.md). If you change the CLI or config, update [`README.md`](./README.md). - **Add tests** for new behavior where practical. diff --git a/Cargo.lock b/Cargo.lock index 9492cfe..d091332 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -282,6 +282,7 @@ dependencies = [ "chrono", "clap", "dirs", + "edison-tunnel-protocol", "futures-util", "once_cell", "reqwest", @@ -293,10 +294,17 @@ dependencies = [ "toml", "tracing", "tracing-subscriber", - "tunnel-protocol", "url", ] +[[package]] +name = "edison-tunnel-protocol" +version = "0.0.1" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1561,14 +1569,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "tunnel-protocol" -version = "0.0.1" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "typenum" version = "1.20.0" diff --git a/Cargo.toml b/Cargo.toml index e08d8ee..1e88875 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ "crates/edison-stdiod", - "crates/tunnel-protocol", + "crates/edison-tunnel-protocol", ] [workspace.package] @@ -10,6 +10,8 @@ edition = "2021" version = "0.0.1" license = "AGPL-3.0-only" authors = ["Edison Watch"] +repository = "https://github.com/Edison-Watch/stdiod" +homepage = "https://github.com/Edison-Watch/stdiod" [workspace.dependencies] anyhow = "1" @@ -29,4 +31,4 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } # Workspace member crates -tunnel-protocol = { path = "crates/tunnel-protocol" } +edison-tunnel-protocol = { path = "crates/edison-tunnel-protocol", version = "0.0.1" } diff --git a/README.md b/README.md index 6a0088f..7512892 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ - **Child supervision.** The backend pushes a desired set of servers; the daemon spawns/stops the matching subprocesses and pumps their stdio. - **Survival.** It reconnects with backoff across network blips and machine sleep/resume, and reconciles desired state on every (re)connect. -See [`ARCHITECTURE.md`](./ARCHITECTURE.md) for the full design and [`schema/tunnel-protocol.json`](./schema/tunnel-protocol.json) for the wire protocol - the single source of truth for the frame types. +See [`ARCHITECTURE.md`](./ARCHITECTURE.md) for the full design and [`schema/edison-tunnel-protocol.json`](./schema/edison-tunnel-protocol.json) for the wire protocol - the single source of truth for the frame types. ## Install @@ -223,7 +223,7 @@ cargo fmt --all --check # formatting cargo clippy --workspace --all-targets -- -D warnings # lints ``` -The `tunnel-protocol` crate's Rust types are generated from [`schema/tunnel-protocol.json`](./schema/tunnel-protocol.json) - keep the schema and the generated types in lock-step. +The `edison-tunnel-protocol` crate's Rust types are generated from [`schema/edison-tunnel-protocol.json`](./schema/edison-tunnel-protocol.json) - keep the schema and the generated types in lock-step. [`dev/spike/`](./dev/spike/) holds a throwaway v0 Python prototype that validated the wire protocol before the Rust daemon was written; it is kept as a historical record and is not part of the build. diff --git a/crates/edison-stdiod/Cargo.toml b/crates/edison-stdiod/Cargo.toml index 8a0d120..d9c42f1 100644 --- a/crates/edison-stdiod/Cargo.toml +++ b/crates/edison-stdiod/Cargo.toml @@ -5,6 +5,7 @@ version.workspace = true license.workspace = true authors.workspace = true description = "Edison Watch stdio MCP server daemon - bridges local stdio MCP servers to the backend tunnel." +readme = "../../README.md" [[bin]] name = "edison-stdiod" @@ -26,5 +27,5 @@ tokio-tungstenite = { workspace = true } toml = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } -tunnel-protocol = { workspace = true } +edison-tunnel-protocol = { workspace = true } url = "2" diff --git a/crates/edison-stdiod/src/config.rs b/crates/edison-stdiod/src/config.rs index 907f84f..7de61c4 100644 --- a/crates/edison-stdiod/src/config.rs +++ b/crates/edison-stdiod/src/config.rs @@ -140,13 +140,13 @@ fn missing(name: &str, flag: &str) -> anyhow::Error { } /// Detected OS, mapped to the wire-protocol `Os` enum. -pub fn current_os() -> tunnel_protocol::Os { +pub fn current_os() -> edison_tunnel_protocol::Os { if cfg!(target_os = "macos") { - tunnel_protocol::Os::Macos + edison_tunnel_protocol::Os::Macos } else if cfg!(target_os = "linux") { - tunnel_protocol::Os::Linux + edison_tunnel_protocol::Os::Linux } else { - tunnel_protocol::Os::Windows + edison_tunnel_protocol::Os::Windows } } diff --git a/crates/edison-stdiod/src/daemon.rs b/crates/edison-stdiod/src/daemon.rs index 0e81a04..59a59c4 100644 --- a/crates/edison-stdiod/src/daemon.rs +++ b/crates/edison-stdiod/src/daemon.rs @@ -21,13 +21,13 @@ use std::time::{Duration, Instant, SystemTime}; use anyhow::{bail, Result}; use clap::Args; -use tokio::sync::{mpsc, Mutex}; -use tokio::time::sleep; -use tracing::{debug, info, warn}; -use tunnel_protocol::{ +use edison_tunnel_protocol::{ ClientHello, DesiredServer, DesiredStateUpdate, McpFrame, ServerHello, ServerSpawnResult, ServerSpecUpdate, TunnelError, TunnelFrame, PROTOCOL_VERSION, }; +use tokio::sync::{mpsc, Mutex}; +use tokio::time::sleep; +use tracing::{debug, info, warn}; use crate::config; use crate::env_store::{resolve_env_for_spawn, substitute_templated_args, EnvStore}; diff --git a/crates/edison-stdiod/src/proc.rs b/crates/edison-stdiod/src/proc.rs index fe75370..b3881c5 100644 --- a/crates/edison-stdiod/src/proc.rs +++ b/crates/edison-stdiod/src/proc.rs @@ -16,12 +16,12 @@ use std::process::Stdio; use anyhow::{Context, Result}; +use edison_tunnel_protocol::{DesiredServer, McpFrame, TunnelError, TunnelFrame}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::process::{Child, Command}; use tokio::sync::mpsc; use tokio::task::JoinHandle; use tracing::{debug, info, warn}; -use tunnel_protocol::{DesiredServer, McpFrame, TunnelError, TunnelFrame}; use crate::tunnel::OutgoingHandle; diff --git a/crates/edison-stdiod/src/tunnel.rs b/crates/edison-stdiod/src/tunnel.rs index f1ce2d6..891b9dd 100644 --- a/crates/edison-stdiod/src/tunnel.rs +++ b/crates/edison-stdiod/src/tunnel.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; +use edison_tunnel_protocol::TunnelFrame; use futures_util::{SinkExt, StreamExt}; use tokio::net::TcpStream; use tokio::sync::mpsc; @@ -20,7 +21,6 @@ use tokio_tungstenite::tungstenite::http::HeaderValue; use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; use tracing::{debug, info, warn}; -use tunnel_protocol::TunnelFrame; pub type WsStream = WebSocketStream>; diff --git a/crates/tunnel-protocol/Cargo.toml b/crates/edison-tunnel-protocol/Cargo.toml similarity index 89% rename from crates/tunnel-protocol/Cargo.toml rename to crates/edison-tunnel-protocol/Cargo.toml index 68e9bb9..3bb7150 100644 --- a/crates/tunnel-protocol/Cargo.toml +++ b/crates/edison-tunnel-protocol/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tunnel-protocol" +name = "edison-tunnel-protocol" edition.workspace = true version.workspace = true license.workspace = true diff --git a/crates/tunnel-protocol/src/lib.rs b/crates/edison-tunnel-protocol/src/lib.rs similarity index 100% rename from crates/tunnel-protocol/src/lib.rs rename to crates/edison-tunnel-protocol/src/lib.rs diff --git a/dev/spike/README.md b/dev/spike/README.md index cec9492..d2d2929 100644 --- a/dev/spike/README.md +++ b/dev/spike/README.md @@ -10,7 +10,7 @@ kept here as a historical record, not part of the daemon build. ``` stub_mcp_server.py FastMCP stdio server: add, slow_count, ask_sample, crash -tunnel_protocol.py Pydantic models for the wire envelope (ClientHello, +edison_tunnel_protocol.py Pydantic models for the wire envelope (ClientHello, ServerHello, McpFrame, TunnelError) tunnel_transport.py Custom ClientTransport. Wraps SessionMessages as McpFrames; unwraps inbound frames into ClientSession's