+54
-422
Diff
round #3
+8
-8
CLAUDE.md
+8
-8
CLAUDE.md
···
4
4
5
5
## What is Ein
6
6
7
-
Ein is a Rust-based AI agent framework with a client-server architecture. A gRPC server drives an LLM agent loop and executes tools implemented as pluggable WASM modules. Multiple model client plugins are supported (OpenRouter, Anthropic, OpenAI, Ollama). A terminal UI client (`ein-tui`) connects to the server and provides an interactive chat interface. Sessions are persisted to SQLite so conversations can be resumed across reconnects.
7
+
Ein is a Rust-based AI agent framework with a client-server architecture. A gRPC server drives an LLM agent loop and executes tools implemented as pluggable WASM modules. Multiple model client plugins are supported (OpenRouter, Anthropic, OpenAI, Ollama). A terminal UI client (`ein`) connects to the server and provides an interactive chat interface. Sessions are persisted to SQLite so conversations can be resumed across reconnects.
8
8
9
9
## Setup
10
10
11
11
```bash
12
12
rustup target add wasm32-wasip2
13
13
cargo build # Build all crates
14
-
cargo build -p ein-tui # Build just the TUI client
14
+
cargo build -p ein # Build just the TUI client
15
15
cargo build -p eind # Build just the server
16
16
```
17
17
···
45
45
cargo run --bin eind
46
46
47
47
# Terminal 2 โ start the TUI (connects to localhost:50051 by default)
48
-
cargo run --bin ein-tui
48
+
cargo run --bin ein
49
49
50
50
# Optional: connect to a non-default server address
51
-
cargo run -p ein-tui -- http://my-server:50051
51
+
cargo run -p ein -- http://my-server:50051
52
52
```
53
53
54
54
The server creates `~/.ein/sessions.db` on first run to persist session history.
···
59
59
60
60
```
61
61
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
62
-
โ ein-tui โ gRPC โ eind โ
62
+
โ ein โ gRPC โ eind โ
63
63
โ โ (proto) โ โ
64
64
โ Ratatui terminal UI โโโโโโโโโโโบโ Agent loop + tool executor โ
65
65
โ Keyboard / render loop โ โ WASM plugin host โ
···
105
105
106
106
Database migrations live in `eind/migrations/`.
107
107
108
-
### Client config (`crates/ein-tui/src/config.rs`)
108
+
### Client config (`ein/src/config.rs`)
109
109
110
110
`ClientConfig` is loaded from (or created at) `~/.ein/config.json` on TUI startup. Structure mirrors `SessionConfig`. At startup the TUI shows a floating modal asking whether to add the current working directory to `allowed_paths` for that session; this is never persisted to `config.json`.
111
111
···
129
129
130
130
**Plugin loading** (`src/tools.rs`): scans the plugin directory for `.wasm` files and instantiates each as a Wasmtime component. The filename stem (e.g. `ein_bash`) is used as the plugin's config identity to look up its entry in `plugin_configs`; global `allowed_paths`/`allowed_hosts` are merged with any plugin-specific overrides before the WASI context is built. After instantiation, `name()`/`schema()` are called to get the display name (e.g. `"Bash"`) and tool schema exposed to the model. In debug mode both tool and model client plugins are loaded from `./target/wasm32-wasip2/debug/`; in release mode tool plugins come from `~/.ein/plugins/tools/` and model client plugins from `~/.ein/plugins/model_clients/`.
131
131
132
-
### TUI (`crates/ein-tui/`)
132
+
### TUI (`ein/`)
133
133
134
-
Six source files under `crates/ein-tui/src/`:
134
+
Six source files under `ein/src/`:
135
135
136
136
| File | Role |
137
137
|------|------|
+26
-26
Cargo.lock
+26
-26
Cargo.lock
···
970
970
source = "registry+https://github.com/rust-lang/crates.io-index"
971
971
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
972
972
973
+
[[package]]
974
+
name = "ein"
975
+
version = "0.1.8"
976
+
dependencies = [
977
+
"anyhow",
978
+
"chrono",
979
+
"clap",
980
+
"crossterm",
981
+
"dirs",
982
+
"ein-proto",
983
+
"notify",
984
+
"ratatui",
985
+
"reqwest",
986
+
"serde",
987
+
"serde_json",
988
+
"syntect",
989
+
"tar",
990
+
"tokio",
991
+
"tokio-stream",
992
+
"tonic",
993
+
"tracing",
994
+
"tracing-appender",
995
+
"tracing-subscriber",
996
+
"xz2",
997
+
]
998
+
973
999
[[package]]
974
1000
name = "ein-agent"
975
1001
version = "0.1.8"
···
1002
1028
"tonic-build",
1003
1029
]
1004
1030
1005
-
[[package]]
1006
-
name = "ein-tui"
1007
-
version = "0.1.8"
1008
-
dependencies = [
1009
-
"anyhow",
1010
-
"chrono",
1011
-
"clap",
1012
-
"crossterm",
1013
-
"dirs",
1014
-
"ein-proto",
1015
-
"notify",
1016
-
"ratatui",
1017
-
"reqwest",
1018
-
"serde",
1019
-
"serde_json",
1020
-
"syntect",
1021
-
"tar",
1022
-
"tokio",
1023
-
"tokio-stream",
1024
-
"tonic",
1025
-
"tracing",
1026
-
"tracing-appender",
1027
-
"tracing-subscriber",
1028
-
"xz2",
1029
-
]
1030
-
1031
1031
[[package]]
1032
1032
name = "ein_anthropic"
1033
1033
version = "0.1.8"
+3
-3
Cargo.toml
+3
-3
Cargo.toml
···
1
1
[workspace]
2
2
members = [
3
3
"eind",
4
-
"crates/ein-tui",
4
+
"ein",
5
5
"crates/ein-proto",
6
6
"crates/ein-agent",
7
7
"crates/ein-core",
···
9
9
]
10
10
default-members = [
11
11
"eind",
12
-
"crates/ein-tui",
12
+
"ein",
13
13
"crates/ein-proto",
14
14
"crates/ein-agent",
15
15
"crates/ein-core",
···
35
35
"x86_64-unknown-linux-gnu",
36
36
]
37
37
pr-run-mode = "plan"
38
-
members = ["crates/ein-tui"]
38
+
members = ["ein"]
39
39
# Allow manual edits to the generated workflow (we add a protoc install step).
40
40
allow-dirty = ["ci"]
41
41
+10
-10
README.md
+10
-10
README.md
···
4
4
5
5
```
6
6
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
7
-
โ ein-tui โ gRPC โ eind โ
7
+
โ ein โ gRPC โ eind โ
8
8
โ โ (proto) โ โ
9
9
โ Ratatui terminal UI โโโโโโโโโโโบโ Agent loop + tool executor โ
10
10
โ Session picker on startup โ โ WASM plugin host โ
···
23
23
cargo binstall --git https://github.com/mstallmo/ein ein
24
24
```
25
25
26
-
This installs both `ein-tui` (terminal UI) and `eind` (gRPC agent server). You can also install them individually:
26
+
This installs both `ein` (terminal UI) and `eind` (gRPC agent server). You can also install them individually:
27
27
28
28
```bash
29
-
cargo binstall --git https://github.com/mstallmo/ein ein-tui
29
+
cargo binstall --git https://github.com/mstallmo/ein ein
30
30
cargo binstall --git https://github.com/mstallmo/ein eind
31
31
```
32
32
···
134
134
Start the TUI client in another:
135
135
136
136
```bash
137
-
cargo run --bin ein-tui
137
+
cargo run --bin ein
138
138
```
139
139
140
140
The TUI connects to `localhost:50051` by default. To connect to a different address:
141
141
142
142
```bash
143
-
cargo run --bin ein-tui -- http://my-server:50051
143
+
cargo run --bin ein -- http://my-server:50051
144
144
```
145
145
146
146
To enable debug logging to `~/.ein/tui.log`:
147
147
148
148
```bash
149
-
cargo run --bin ein-tui -- --debug
149
+
cargo run --bin ein -- --debug
150
150
```
151
151
152
152
On first connection a **session picker** modal appears. Use `โ`/`โ` to navigate, `Enter` to select:
···
270
270
```
271
271
crates/
272
272
ein-proto/ Protocol Buffer definitions (gRPC service + message types)
273
-
eind/ gRPC server โ agent loop, WASM plugin host, session persistence
274
-
ein-tui/ Terminal UI client
273
+
ein/ Terminal UI client
274
+
eind/ gRPC server โ agent loop, WASM plugin host, session persistence
275
275
packages/
276
276
ein_tool/ WASM tool plugin interface (ToolPlugin trait, ToolDef, syscalls)
277
277
ein_bash/ Bash tool plugin
···
302
302
303
303
Sessions are persisted to `~/.ein/sessions.db`. Supplying a previously assigned `session_id` in `SessionConfig` causes the server to restore the full conversation history and resume as if the session never disconnected.
304
304
305
-
### TUI modules (`crates/ein-tui/src/`)
305
+
### TUI modules (`ein/src/`)
306
306
307
307
| File | Role |
308
308
|------|------|
···
329
329
330
330
## Releasing
331
331
332
-
Releases are fully automated via CI using [cargo-dist](https://axodotdev.github.io/cargo-dist/). Only the `crates/ein` meta-package is distributed โ it includes both the `ein-tui` and `eind` binaries.
332
+
Releases are fully automated via CI using [cargo-dist](https://axodotdev.github.io/cargo-dist/).
333
333
334
334
**1. Bump the version**
335
335
-368
crates/ein-tui/src/bootstrap.rs
-368
crates/ein-tui/src/bootstrap.rs
···
1
-
// SPDX-License-Identifier: Apache-2.0
2
-
// Copyright 2026 Mason Stallmo
3
-
4
-
//! Bootstrap logic: downloads `eind` on first run and registers it as a
5
-
//! system service (macOS LaunchAgent or Linux systemd user service).
6
-
7
-
// These items are only called from the #[cfg(not(debug_assertions))] block in
8
-
// lib.rs, so they appear unused in debug builds. That's intentional.
9
-
#![cfg_attr(debug_assertions, allow(dead_code))]
10
-
11
-
use anyhow::{Context, Result};
12
-
use std::{
13
-
io,
14
-
os::unix::fs::PermissionsExt,
15
-
path::{Path, PathBuf},
16
-
};
17
-
use tar::Archive;
18
-
use tokio::{fs, process::Command, task};
19
-
use xz2::read::XzDecoder;
20
-
21
-
const GITHUB_REPO: &str = "mstallmo/ein";
22
-
23
-
/// Path where `ein` installs the server binary: `~/.ein/bin/eind`.
24
-
pub fn server_bin_path() -> PathBuf {
25
-
dirs::home_dir()
26
-
.expect("home directory not found")
27
-
.join(".ein")
28
-
.join("bin")
29
-
.join("eind")
30
-
}
31
-
32
-
/// Compile-time target triple used to select the right GitHub release asset.
33
-
pub fn target_triple() -> &'static str {
34
-
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
35
-
return "aarch64-apple-darwin";
36
-
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
37
-
return "x86_64-apple-darwin";
38
-
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
39
-
return "aarch64-unknown-linux-gnu";
40
-
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
41
-
return "x86_64-unknown-linux-gnu";
42
-
#[allow(unreachable_code)]
43
-
""
44
-
}
45
-
46
-
/// Downloads the `eind` binary for the current platform from GitHub
47
-
/// releases and writes it to `~/.ein/bin/eind` with executable permissions.
48
-
pub async fn download_server(version: &str) -> Result<()> {
49
-
let ver = version.trim_start_matches('v');
50
-
let tag = format!("v{ver}");
51
-
let triple = target_triple();
52
-
// cargo-dist names archives as "{package}-{triple}.tar.xz" (no version in filename).
53
-
let archive_name = format!("eind-{triple}.tar.xz");
54
-
let url = format!("https://github.com/{GITHUB_REPO}/releases/download/{tag}/{archive_name}");
55
-
56
-
let dest = server_bin_path();
57
-
fs::create_dir_all(dest.parent().unwrap())
58
-
.await
59
-
.context("failed to create ~/.ein/bin")?;
60
-
61
-
println!("Downloading {url}...");
62
-
63
-
let response = reqwest::get(&url)
64
-
.await
65
-
.with_context(|| format!("failed to fetch {url}"))?;
66
-
67
-
if !response.status().is_success() {
68
-
anyhow::bail!("download failed: HTTP {}", response.status());
69
-
}
70
-
71
-
let bytes = response
72
-
.bytes()
73
-
.await
74
-
.context("failed to read response body")?;
75
-
76
-
let dest_clone = dest.clone();
77
-
task::spawn_blocking(move || extract_server(&bytes, &dest_clone))
78
-
.await
79
-
.context("extraction task panicked")??;
80
-
81
-
// Make the binary executable.
82
-
let mut perms = fs::metadata(&dest).await?.permissions();
83
-
perms.set_mode(0o755);
84
-
fs::set_permissions(&dest, perms).await?;
85
-
86
-
println!("eind installed to {}", dest.display());
87
-
Ok(())
88
-
}
89
-
90
-
/// Extracts the `eind` binary from a tar.xz archive into `dest`.
91
-
fn extract_server(bytes: &[u8], dest: &Path) -> Result<()> {
92
-
let xz = XzDecoder::new(io::Cursor::new(bytes));
93
-
let mut archive = Archive::new(xz);
94
-
95
-
for entry in archive
96
-
.entries()
97
-
.context("failed to read archive entries")?
98
-
{
99
-
let mut entry = entry.context("corrupt archive entry")?;
100
-
let entry_path = entry.path().context("entry has no path")?;
101
-
102
-
// The archive contains exactly one file: the `eind` binary.
103
-
// Accept it regardless of any leading directory component.
104
-
let file_name = entry_path
105
-
.file_name()
106
-
.and_then(|n| n.to_str())
107
-
.unwrap_or("");
108
-
109
-
if file_name == "eind" {
110
-
let mut file = std::fs::File::create(dest)
111
-
.with_context(|| format!("failed to create {}", dest.display()))?;
112
-
io::copy(&mut entry, &mut file).context("failed to write eind")?;
113
-
return Ok(());
114
-
}
115
-
}
116
-
117
-
anyhow::bail!("eind binary not found in archive")
118
-
}
119
-
120
-
// ---------------------------------------------------------------------------
121
-
// Service registration
122
-
// ---------------------------------------------------------------------------
123
-
124
-
/// Ensures `eind` is registered as a system service.
125
-
///
126
-
/// On macOS, installs a LaunchAgent plist and loads it.
127
-
/// On Linux, writes a systemd user unit and enables it.
128
-
/// On other platforms, does nothing (the TUI's retry loop handles reconnects).
129
-
pub async fn ensure_service_installed() -> Result<()> {
130
-
#[cfg(target_os = "macos")]
131
-
return ensure_launchagent_installed().await;
132
-
133
-
#[cfg(target_os = "linux")]
134
-
return ensure_systemd_installed().await;
135
-
136
-
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
137
-
Ok(())
138
-
}
139
-
140
-
// ---------------------------------------------------------------------------
141
-
// Uninstall
142
-
// ---------------------------------------------------------------------------
143
-
144
-
/// Stops and removes the `eind` service and binary installed by
145
-
/// [`ensure_service_installed`] and [`download_server`].
146
-
///
147
-
/// Returns a list of completed step descriptions for display in the TUI.
148
-
/// User config and session data in `~/.ein/` are left intact.
149
-
pub async fn uninstall() -> Result<Vec<String>> {
150
-
let mut steps: Vec<String> = Vec::new();
151
-
#[cfg(target_os = "macos")]
152
-
uninstall_launchagent(&mut steps).await?;
153
-
#[cfg(target_os = "linux")]
154
-
uninstall_systemd(&mut steps).await?;
155
-
remove_server_binary(&mut steps).await?;
156
-
Ok(steps)
157
-
}
158
-
159
-
async fn remove_server_binary(steps: &mut Vec<String>) -> Result<()> {
160
-
let path = server_bin_path();
161
-
if path.exists() {
162
-
fs::remove_file(&path)
163
-
.await
164
-
.with_context(|| format!("failed to remove {}", path.display()))?;
165
-
steps.push(format!("Removed {}", path.display()));
166
-
}
167
-
Ok(())
168
-
}
169
-
170
-
#[cfg(target_os = "macos")]
171
-
async fn uninstall_launchagent(steps: &mut Vec<String>) -> Result<()> {
172
-
let plist = launchagent_plist_path();
173
-
// Ignore errors โ the service may already be stopped/unloaded.
174
-
let _ = Command::new("launchctl")
175
-
.args(["unload", plist.to_str().unwrap_or("")])
176
-
.output()
177
-
.await;
178
-
if plist.exists() {
179
-
fs::remove_file(&plist)
180
-
.await
181
-
.with_context(|| format!("failed to remove {}", plist.display()))?;
182
-
steps.push(format!("Removed LaunchAgent ({})", LAUNCH_AGENT_LABEL));
183
-
}
184
-
Ok(())
185
-
}
186
-
187
-
#[cfg(target_os = "linux")]
188
-
async fn uninstall_systemd(steps: &mut Vec<String>) -> Result<()> {
189
-
let unit = systemd_unit_path();
190
-
let _ = Command::new("systemctl")
191
-
.args(["--user", "stop", SYSTEMD_SERVICE_NAME])
192
-
.output()
193
-
.await;
194
-
let _ = Command::new("systemctl")
195
-
.args(["--user", "disable", SYSTEMD_SERVICE_NAME])
196
-
.output()
197
-
.await;
198
-
if unit.exists() {
199
-
fs::remove_file(&unit)
200
-
.await
201
-
.with_context(|| format!("failed to remove {}", unit.display()))?;
202
-
steps.push(format!(
203
-
"Removed systemd user service ({})",
204
-
SYSTEMD_SERVICE_NAME
205
-
));
206
-
}
207
-
let _ = Command::new("systemctl")
208
-
.args(["--user", "daemon-reload"])
209
-
.output()
210
-
.await;
211
-
Ok(())
212
-
}
213
-
214
-
// ---------------------------------------------------------------------------
215
-
// macOS LaunchAgent
216
-
// ---------------------------------------------------------------------------
217
-
218
-
#[cfg(target_os = "macos")]
219
-
const LAUNCH_AGENT_LABEL: &str = "com.ein.server";
220
-
221
-
#[cfg(target_os = "macos")]
222
-
fn launchagent_plist_path() -> PathBuf {
223
-
dirs::home_dir()
224
-
.expect("home directory not found")
225
-
.join("Library")
226
-
.join("LaunchAgents")
227
-
.join(format!("{LAUNCH_AGENT_LABEL}.plist"))
228
-
}
229
-
230
-
#[cfg(target_os = "macos")]
231
-
async fn ensure_launchagent_installed() -> Result<()> {
232
-
// Check if already loaded.
233
-
let status = Command::new("launchctl")
234
-
.args(["list", LAUNCH_AGENT_LABEL])
235
-
.output()
236
-
.await
237
-
.context("launchctl not found")?;
238
-
239
-
if status.status.success() {
240
-
return Ok(()); // Already running.
241
-
}
242
-
243
-
let plist_path = launchagent_plist_path();
244
-
let bin = server_bin_path();
245
-
let log = dirs::home_dir()
246
-
.expect("home directory not found")
247
-
.join(".ein")
248
-
.join("server.log");
249
-
250
-
let plist = format!(
251
-
r#"<?xml version="1.0" encoding="UTF-8"?>
252
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
253
-
<plist version="1.0">
254
-
<dict>
255
-
<key>Label</key>
256
-
<string>{LAUNCH_AGENT_LABEL}</string>
257
-
<key>ProgramArguments</key>
258
-
<array>
259
-
<string>{bin}</string>
260
-
</array>
261
-
<key>RunAtLoad</key>
262
-
<true/>
263
-
<key>KeepAlive</key>
264
-
<true/>
265
-
<key>StandardOutPath</key>
266
-
<string>{log}</string>
267
-
<key>StandardErrorPath</key>
268
-
<string>{log}</string>
269
-
</dict>
270
-
</plist>
271
-
"#,
272
-
LAUNCH_AGENT_LABEL = LAUNCH_AGENT_LABEL,
273
-
bin = bin.display(),
274
-
log = log.display(),
275
-
);
276
-
277
-
fs::create_dir_all(plist_path.parent().unwrap())
278
-
.await
279
-
.context("failed to create LaunchAgents directory")?;
280
-
fs::write(&plist_path, plist)
281
-
.await
282
-
.context("failed to write plist")?;
283
-
284
-
let output = Command::new("launchctl")
285
-
.args(["load", plist_path.to_str().unwrap()])
286
-
.output()
287
-
.await
288
-
.context("failed to run launchctl load")?;
289
-
290
-
if !output.status.success() {
291
-
let stderr = String::from_utf8_lossy(&output.stderr);
292
-
anyhow::bail!("launchctl load failed: {stderr}");
293
-
}
294
-
295
-
println!("eind registered as LaunchAgent ({LAUNCH_AGENT_LABEL})");
296
-
Ok(())
297
-
}
298
-
299
-
// ---------------------------------------------------------------------------
300
-
// Linux systemd user service
301
-
// ---------------------------------------------------------------------------
302
-
303
-
#[cfg(target_os = "linux")]
304
-
const SYSTEMD_SERVICE_NAME: &str = "eind";
305
-
306
-
#[cfg(target_os = "linux")]
307
-
fn systemd_unit_path() -> PathBuf {
308
-
dirs::home_dir()
309
-
.expect("home directory not found")
310
-
.join(".config")
311
-
.join("systemd")
312
-
.join("user")
313
-
.join(format!("{SYSTEMD_SERVICE_NAME}.service"))
314
-
}
315
-
316
-
#[cfg(target_os = "linux")]
317
-
async fn ensure_systemd_installed() -> Result<()> {
318
-
// Check if already enabled.
319
-
let status = Command::new("systemctl")
320
-
.args(["--user", "is-enabled", SYSTEMD_SERVICE_NAME])
321
-
.output()
322
-
.await
323
-
.context("systemctl not found")?;
324
-
325
-
if status.status.success() {
326
-
return Ok(()); // Already enabled.
327
-
}
328
-
329
-
let unit_path = systemd_unit_path();
330
-
let bin = server_bin_path();
331
-
332
-
let unit = format!(
333
-
"[Unit]\nDescription=Ein server\n\n[Service]\nExecStart={bin}\nRestart=always\n\n[Install]\nWantedBy=default.target\n",
334
-
bin = bin.display(),
335
-
);
336
-
337
-
fs::create_dir_all(unit_path.parent().unwrap())
338
-
.await
339
-
.context("failed to create systemd user directory")?;
340
-
fs::write(&unit_path, unit)
341
-
.await
342
-
.context("failed to write systemd unit")?;
343
-
344
-
let reload = Command::new("systemctl")
345
-
.args(["--user", "daemon-reload"])
346
-
.output()
347
-
.await
348
-
.context("failed to run systemctl daemon-reload")?;
349
-
350
-
if !reload.status.success() {
351
-
let stderr = String::from_utf8_lossy(&reload.stderr);
352
-
anyhow::bail!("systemctl daemon-reload failed: {stderr}");
353
-
}
354
-
355
-
let enable = Command::new("systemctl")
356
-
.args(["--user", "enable", "--now", SYSTEMD_SERVICE_NAME])
357
-
.output()
358
-
.await
359
-
.context("failed to run systemctl enable")?;
360
-
361
-
if !enable.status.success() {
362
-
let stderr = String::from_utf8_lossy(&enable.stderr);
363
-
anyhow::bail!("systemctl enable failed: {stderr}");
364
-
}
365
-
366
-
println!("eind registered as systemd user service ({SYSTEMD_SERVICE_NAME})");
367
-
Ok(())
368
-
}
+3
-3
crates/ein-tui/Cargo.toml
ein/Cargo.toml
+3
-3
crates/ein-tui/Cargo.toml
ein/Cargo.toml
···
1
1
[package]
2
-
name = "ein-tui"
2
+
name = "ein"
3
3
version.workspace = true
4
4
edition.workspace = true
5
5
authors.workspace = true
···
8
8
homepage.workspace = true
9
9
10
10
[lib]
11
-
name = "ein_tui"
11
+
name = "ein"
12
12
path = "src/lib.rs"
13
13
14
14
[[bin]]
···
23
23
crossterm = { version = "0.28", features = ["event-stream"] }
24
24
notify = "6"
25
25
dirs = "6.0.0"
26
-
ein-proto = { path = "../ein-proto" }
26
+
ein-proto = { path = "../crates/ein-proto" }
27
27
ratatui = { version = "0.29", features = ["unstable-rendered-line-info"] }
28
28
serde = { workspace = true }
29
29
serde_json = { workspace = true }
crates/ein-tui/src/app.rs
ein/src/app.rs
crates/ein-tui/src/app.rs
ein/src/app.rs
crates/ein-tui/src/config.rs
ein/src/config.rs
crates/ein-tui/src/config.rs
ein/src/config.rs
crates/ein-tui/src/connection.rs
ein/src/connection.rs
crates/ein-tui/src/connection.rs
ein/src/connection.rs
crates/ein-tui/src/input.rs
ein/src/input.rs
crates/ein-tui/src/input.rs
ein/src/input.rs
+2
-2
crates/ein-tui/src/lib.rs
ein/src/lib.rs
+2
-2
crates/ein-tui/src/lib.rs
ein/src/lib.rs
···
3
3
4
4
//! Ein TUI library.
5
5
//!
6
-
//! Exposes [`run`] so both the standalone `ein-tui` binary and the `ein`
6
+
//! Exposes [`run`] so both the standalone `ein` binary and the `ein`
7
7
//! meta-package binary can share the same entry-point without duplicating code.
8
8
9
9
mod app;
···
96
96
None
97
97
};
98
98
99
-
info!(server_addr = %args.server_addr, "ein-tui starting");
99
+
info!(server_addr = %args.server_addr, "ein starting");
100
100
101
101
// In release builds: download eind if absent, then register it as a
102
102
// system service. Runs before raw mode so stdout is visible for progress.
+2
-2
crates/ein-tui/src/main.rs
ein/src/main.rs
+2
-2
crates/ein-tui/src/main.rs
ein/src/main.rs
crates/ein-tui/src/render.rs
ein/src/render.rs
crates/ein-tui/src/render.rs
ein/src/render.rs
History
7 rounds
0 comments
mstallmo.com
submitted
#6
1 commit
expand
collapse
Rename
ein-tui to ein. Move from crates/ directory to top level project directory.
merge conflicts detected
expand
collapse
expand
collapse
- CLAUDE.md:4
- Cargo.lock:970
- Cargo.toml:1
- README.md:4
expand 0 comments
mstallmo.com
submitted
#5
1 commit
expand
collapse
Rename
ein-tui to ein. Move from crates/ directory to top level project directory.
expand 0 comments
mstallmo.com
submitted
#4
1 commit
expand
collapse
Rename
ein-tui to ein. Move from crates/ directory to top level project directory.
expand 0 comments
mstallmo.com
submitted
#3
1 commit
expand
collapse
Rename
ein-tui to ein. Move from crates/ directory to top level project directory.
expand 0 comments
mstallmo.com
submitted
#2
1 commit
expand
collapse
Rename
ein-tui to ein. Move from crates/ directory to top level project directory.
expand 0 comments
mstallmo.com
submitted
#1
1 commit
expand
collapse
Rename
ein-tui to ein. Move from crates/ directory to top level project directory.
expand 0 comments
mstallmo.com
submitted
#0
1 commit
expand
collapse
Rename
ein-tui to ein. Move from crates/ directory to top level project directory.