Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ jobs:
cache: 'npm'

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
uses: dtolnay/rust-toolchain@stable

- name: Install system packages (GTK, WebKit, AppImage deps)
run: |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Termalime",
"private": true,
"version": "0.2.0",
"version": "0.3.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
14 changes: 13 additions & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "Termalime"
version = "0.2.0"
version = "0.3.0"
description = "A Terminal with an Ollama Co-Pilot"
authors = ["Davey Mason"]
edition = "2021"
Expand Down Expand Up @@ -30,4 +30,5 @@ reqwest = { version = "0.12", default-features = false, features = ["json", "str
tokio = { version = "=1.40.0", features = ["macros", "rt-multi-thread", "sync"] }
futures-util = "0.3"
json5 = "0.4"
hostname = "0.4"

115 changes: 113 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,121 @@ async fn get_terminal_context(
#[tauri::command]
async fn check_ollama() -> Result<bool, String> {
let response = HTTP_CLIENT
.get("http://127.0.0.1:11434/api/tags")
.send()
.await;

match response {
Ok(res) => Ok(res.status().is_success()),
Err(_) => Ok(false),
}
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct SystemContext {
hostname: Option<String>,
username: Option<String>,
local_ip: Option<String>,
git_branch: Option<String>,
cwd: Option<String>,
shell: Option<String>,
ollama_online: bool,
}

#[tauri::command]
async fn get_system_context(_session_id: Option<String>) -> Result<SystemContext, String> {
use std::env;
use std::process::Command;

// Get hostname
let hostname = hostname::get()
.ok()
.and_then(|h| h.into_string().ok());

// Get username
let username = env::var("USER")
.or_else(|_| env::var("USERNAME"))
.ok();

// Get shell
let shell = env::var("SHELL")
.ok()
.and_then(|s| s.split('/').last().map(String::from));

// Get current working directory
let cwd = env::current_dir()
.ok()
.and_then(|p| p.to_str().map(String::from));

// Get git branch - try from the executable's directory first (likely the project)
let exe_dir = env::current_exe()
.ok()
.and_then(|p| p.parent().map(|d| d.to_path_buf()))
// Go up from target/debug to project root
.and_then(|p| p.parent().map(|d| d.to_path_buf()))
.and_then(|p| p.parent().map(|d| d.to_path_buf()));

let git_branch = exe_dir.as_ref()
.and_then(|dir| {
Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.current_dir(dir)
.stderr(std::process::Stdio::null())
.output()
.ok()
.filter(|output| output.status.success())
.and_then(|output| String::from_utf8(output.stdout).ok())
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
})
.or_else(|| {
// Fallback: try from cwd
cwd.as_ref().and_then(|dir| {
Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.current_dir(dir)
.stderr(std::process::Stdio::null())
.output()
.ok()
.filter(|output| output.status.success())
.and_then(|output| String::from_utf8(output.stdout).ok())
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
})
});

// Get local IP address
let local_ip = get_local_ip();

// Check if Ollama is online
let ollama_online = HTTP_CLIENT
.get("http://127.0.0.1:11434/api/tags")
.send()
.await
.map_err(|err| err.to_string())?;
.map(|res| res.status().is_success())
.unwrap_or(false);

Ok(SystemContext {
hostname,
username,
local_ip,
git_branch,
cwd,
shell,
ollama_online,
})
}

Ok(response.status().is_success())
fn get_local_ip() -> Option<String> {
use std::net::UdpSocket;

// Create a UDP socket and "connect" to a public address
// This doesn't actually send data, just determines the local interface
let socket = UdpSocket::bind("0.0.0.0:0").ok()?;
socket.connect("8.8.8.8:80").ok()?;
let local_addr = socket.local_addr().ok()?;
Some(local_addr.ip().to_string())
}

#[tauri::command]
Expand Down Expand Up @@ -1153,6 +1262,7 @@ struct OllamaResponseChunk {

#[derive(Deserialize)]
struct OllamaMessage {
#[allow(dead_code)]
role: String,
content: String,
}
Expand Down Expand Up @@ -1190,6 +1300,7 @@ pub fn run() {
check_ollama,
list_ollama_models,
get_terminal_context,
get_system_context,
analyze_command
])
.run(tauri::generate_context!())
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ impl PtyRegistry {
pub struct PtySession {
pub id: String,
master: Box<dyn MasterPty + Send>,
#[allow(dead_code)] // Kept alive to maintain the child process
child: Box<dyn Child + Send>,
writer: Box<dyn Write + Send>,
reader: Option<Box<dyn Read + Send>>,
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Termalime",
"version": "0.2.0",
"version": "0.3.0",
"identifier": "termaline",
"build": {
"beforeDevCommand": "npm run dev",
Expand Down
Loading