[package] name = "termul-manager" version = "0.4.21" description = "Project-aware terminal that treats workspaces as first-class citizens" authors = ["gnoviawan", "mannnrachman (Tauri port)"] edition = "2021" # ACP 1.3.0 is edition 2024 and requires Rust ≥ 1.88; `std::fs::File::try_lock` # (data-dir locks, AD-14) stabilised in 1.89, so the MSRV is 1.89. rust-version = "1.89" # Two binaries live in this crate: the desktop app (`termul-manager`, the # implicit src/main.rs) and the standalone headless server (`termul-server`). # Pin `cargo run` / `tauri dev` to the desktop app so Cargo doesn't error on # ambiguity. The standalone binary is built explicitly via # `cargo build --bin termul-server --features standalone-server`. default-run = "termul-manager" [lib] name = "termul_manager_lib" crate-type = ["staticlib", "cdylib", "rlib"] # Standalone headless server (Story 1.2). Gated so default `cargo build` / `tauri # build` do NOT build or bundle it (Tauri bundler copies every [[bin]] it # discovers — see tauri#15325). Source lives OUTSIDE `src/bin/` so stage-2 # disk scan cannot re-add it. Build with: # cargo build --bin termul-server --features standalone-server [[bin]] name = "termul-server" path = "src/server_main.rs" required-features = ["standalone-server"] [features] default = [] # Enables the termul-server binary entry (server_main.rs, required-features on # the [[bin]] target). rust-embed is always-on (the desktop's in-process # shared-live server serves the SAME embedded bundle in a release install # without dist-web/ on disk). # # The `server_update` library module + its `minisign-verify` dependency are # intentionally NOT feature-gated: the spec's verification gate runs # `cargo test` (default features) and expects the new `server_update` tests — # including signature verification — to execute there. Only the standalone # *binary* (and its wiring in server_main.rs) is gated, which preserves the # "default `cargo build` / `tauri build` do NOT bundle the standalone server # binary" invariant. standalone-server = [] # Forwards to `tauri/custom-protocol`, which makes the Tauri build script # embed `frontendDist` instead of loading `devUrl`. The Tauri CLI (`tauri # build`) enables it itself; raw `cargo build --release` packagers must pass # `--features custom-protocol`. `build.rs` fails a release desktop build that # lacks it. custom-protocol = ["tauri/custom-protocol"] [build-dependencies] tauri-build = { version = "2", features = [] } [dependencies] # unstable is required for child/multi-webview support used by BrowserTabManager tauri = { version = "2.0", features = ["unstable", "devtools", "tray-icon"] } tauri-plugin-os = "2" tauri-plugin-store = "2" notify = "8" tauri-plugin-fs = { version = "2", features = ["watch"] } tauri-plugin-dialog = "2" tauri-plugin-clipboard-manager = "2" tauri-plugin-updater = "2" tauri-plugin-process = "2" tauri-plugin-shell = "2" tauri-plugin-opener = "2" tauri-plugin-notification = "2" tauri-plugin-log = "2" tauri-plugin-single-instance = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" chrono = "0.4" # ADR-004.6: opt-in, read-only fetch of the ACP Registry (identity/discovery # only). rustls-tls matches the backend already pulled in by the updater plugin. reqwest = { version = "0.13", default-features = false, features = ["json", "rustls", "http2", "stream"] } # ACP registry binary install (zip / tar.gz archives) zip = "2" tar = "0.4" flate2 = "1" # PTY and async runtime portable-pty = "0.9" anyhow = "1" tokio = { version = "1", features = ["full", "test-util"] } parking_lot = "0.12" futures = "0.3" futures-concurrency = "7" async-process = "2" # ACP (Agent Client Protocol) — ADR-003 P0. # Upgraded from vendored 0.12.1 to crates.io 1.3.0: # - The Windows console-hiding patch (CREATE_NO_WINDOW) landed upstream in # 0.15.1 (#215), so the vendored copy and [patch.crates-io] redirect are no # longer needed — we now consume crates.io directly. # - `unstable_session_model` and `unstable_session_usage` were stabilized in # 0.14.0, so the feature flags are dropped. # - 1.3.0 adds Unix process-group teardown on ChildGuard drop and correct EOF # handling, improving agent-subprocess reliability on all platforms. agent-client-protocol = { version = "1.3", features = ["unstable_auth_methods", "unstable_elicitation"] } # rmcp (Rust MCP SDK). Two consumers in this crate: # 1. `src/acp/mcp_probe.rs` — on-demand CLIENT probe (`initialize` + `tools/list` # against a renderer-supplied server config). Uses `client`, # `transport-child-process`, `transport-streamable-http-client-reqwest`, # `client-side-sse` (rmcp's SSE stream parser, consumed by the # streamable-http client which still speaks `text/event-stream` after rmcp # 1.7 dropped the standalone legacy SSE transport — CHANGELOG #562). # 2. `src/acp/host_mcp/` — the host-injected `termul` MCP SERVER (exposes the `plan` tool), served # over stdio by a self-spawned child of the host binary. Uses `server`, # `transport-io` (`rmcp::transport::io::stdio()`), and `macros` # (`#[tool_router]` / `#[tool]` / `#[tool_handler]`; the `#[tool]` macro # re-exports `rmcp::schemars` for `JsonSchema` derives — no direct `schemars` # dep needed). 3.5.0 speaks MCP 2026-07-28 (`server/discover`) and still # accepts the legacy initialize handshake. rmcp = { version = "3.5.0", features = [ "client", "transport-child-process", "transport-streamable-http-client-reqwest", "client-side-sse", "server", "transport-io", "macros", "auth", ] } # OAuth2 — needed for the `TokenResponse` trait used by the MCP OAuth flow # (rmcp's `auth` feature pulls this transitively, but we use it directly). oauth2 = "5.0" # Regex and parsing regex = "1" lazy_static = "1.4" # SIMD newline / needle search for the host-start JSONL sweep (already in the # lock tree through serde_json and regex). memchr = "2" # UUID generation uuid = { version = "1", features = ["v4", "serde"] } # TCP keepalive on SSH sockets, so a dropped network is detected promptly. socket2 = "0.6" # Native SSH client: sessions, SFTP and port forwarding share one connection. # Pure Rust on the aws-lc-rs backend already in the tree, so every platform # gets the same algorithms (ed25519, ECDSA, RSA-SHA2, encrypted OpenSSH keys) # without an OpenSSL/Perl toolchain. russh = "0.64" russh-sftp = "3.0" # HMAC-SHA1 for hashed `known_hosts` entries (`|1|salt|hash`); same versions # russh already uses. hmac = "0.13" sha1 = "0.11" # Secure storage (OS keychain for secure credential storage). # IMPORTANT: keyring selects its backend at COMPILE TIME via cargo features. # Without an OS-backend feature it silently falls back to an in-memory mock # store, so set_password "succeeds" but get_password always returns NoEntry # (credentials never persist). Enable the native backend per OS below. keyring = "3.6" # Logging log = "0.4" # Remote terminal server (embedded Axum HTTP + WebSocket) # Reuses the existing tokio + hyper 1.x + tower stack already in the dep tree. axum = { version = "0.8", features = ["ws"] } tower-http = { version = "0.6", features = ["cors", "fs", "trace", "compression-gzip"] } # Cryptographic random generation for auth tokens getrandom = "0.2" # Terminal claim credentials (CAP-3 reclaimable terminal leases): SHA-256 # digest-only storage of issued credentials + constant-time comparison. sha2 = "0.10" subtle = "2" # Structured logging for remote server tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } futures-util = { version = "0.3", default-features = false } # async trait support (CAP-6 / Story 9: install service Downloader/Extractor # injection seams). Already in the dep tree transitively; declared here so # the install service can use the macro directly. async-trait = "0.1" # Embed the Vite web bundle (dist-web/) into the binary so the standalone # termul-server AND the desktop's in-process shared-live server serve the web # client without dist-web/ on disk. #[allow_missing] keeps dev/CI-compile # green when the bundle is absent; the release build fails clearly via # build.rs when it is missing. rust-embed = { version = "8.12", features = ["mime-guess"] } # Standalone `termul-server` self-update (opt-in, default off): verifies # downloaded binary signatures against the same minisign keypair the desktop # updater uses. Non-optional so the `server_update` module + its tests (incl. # signature verification) compile and run under default `cargo test`, per the # spec's verification gate. The standalone *binary* that actually wires the # subsystem remains gated by the `standalone-server` feature. minisign-verify = "0.2" # Decodes the outer base64 wrapper around the embedded minisign pubkey (the # `TAURI_SIGNING_PUBLIC_KEY` secret / tauri.conf.json `pubkey` field both hold # the minisign `.pub` file text, base64-encoded) so `PublicKey::decode` can # parse the raw key the updater verifies against. base64 = "0.22" # SQLite for the agentation annotation service (issue #451, Epic 1). # `bundled` compiles SQLite from source so the binary has no system-SQLite # dependency — matches the single-binary invariant. rusqlite = { version = "0.32", features = ["bundled"] } # tokio utilities for agentation SSE broadcast + graceful shutdown (issue #451). tokio-stream = { version = "0.1", features = ["sync"] } tokio-util = { version = "0.7", features = ["rt"] } [dev-dependencies] tauri = { version = "2.0", features = ["test"] } tower = { version = "0.5", features = ["util"] } # Swap/rollback file tests in `server_update` use a temp dir so they never # touch the real binary path. tempfile = "3" # --- OS keychain backends for `keyring` (see note on the keyring dep above) --- # Windows Credential Manager [target.'cfg(target_os = "windows")'.dependencies] keyring = { version = "3.6", features = ["windows-native"] } notify-rust = "4.18" # macOS Keychain [target.'cfg(target_os = "macos")'.dependencies] keyring = { version = "3.6", features = ["apple-native"] } notify-rust = "4.18" # Linux Secret Service (persistent across reboots) with pure-Rust crypto so we # don't require a system OpenSSL/Perl build toolchain. [target.'cfg(target_os = "linux")'.dependencies] keyring = { version = "3.6", features = ["sync-secret-service", "crypto-rust"] } gtk = "0.18" notify-rust = "4.18" # Symlink-safe (O_NOFOLLOW) open of the persisted web auth token file # (web::auth::open_token_file). Already in the tree transitively. [target.'cfg(unix)'.dependencies] libc = "0.2" [target.'cfg(windows)'.dependencies] winreg = "0.55" # WebView2 CDP for agent browser automation (browser_automation::cdp). # Already in the tree transitively via wry — same major keeps the # ICoreWebView2 type identity shared with `with_webview`'s PlatformWebview. webview2-com = "0.38" windows-core = "0.61" windows-sys = { version = "0.59", features = [ "Win32_UI_WindowsAndMessaging", "Win32_Foundation", # Owner-only DACL on the persisted web auth token file (web::auth). "Win32_Security", "Win32_Security_Authorization", # CreateFileW: the token file gets its owner-only DACL at creation time. "Win32_Storage_FileSystem", # Native junction creation (worktree::junction): DeviceIoControl with # FSCTL_SET_REPARSE_POINT replaces `cmd /C mklink /J`. "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_SystemServices", ] } winapi = { version = "0.3", features = [ "jobapi2", "winbase", "winuser", "consoleapi", "handleapi", "processenv", "processthreadsapi", "synchapi", "minwindef", "winnt", "namedpipeapi", "wincontypes", "ntdef", ] } # The vendored copy of agent-client-protocol and the [patch.crates-io] # redirect were removed when we upgraded to 1.3.0 — the Windows # CREATE_NO_WINDOW console-hiding patch landed upstream in 0.15.1 (#215).