Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96b9c9cdba |
Generated
+2
-2
@@ -403,7 +403,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cassady"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -436,7 +436,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cassady-desktop"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"cassady",
|
||||
"serde",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cassady"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
edition = "2021"
|
||||
description = "Cassady/Cass minimal terminal coding agent"
|
||||
license = "MIT"
|
||||
|
||||
@@ -86,7 +86,7 @@ cass setup
|
||||
cass update
|
||||
```
|
||||
|
||||
`cass --resume` without an id lists saved chats for the current directory. `cass desktop` launches the desktop app in the background when the `cassady-desktop` binary is installed next to `cass` or on `PATH`; after it starts, you can close the terminal. `cass update` checks official GitHub releases and can update `cass`, `cassady`, and the companion desktop binary when present in the selected prebuilt archive. When Cassady exits a chat, it prints a resume command for that session.
|
||||
`cass --resume` without an id lists saved chats for the current directory. `cass desktop` launches the desktop app in the background when a release-built `cassady-desktop` binary is installed next to `cass` or on `PATH`; after it starts, you can close the terminal. Cassady refuses desktop binaries that do not contain the bundled frontend assets because they open as a blank window. `cass update` checks official GitHub releases and can update `cass`, `cassady`, and the companion desktop binary when present in the selected prebuilt archive. When Cassady exits a chat, it prints a resume command for that session.
|
||||
|
||||
Common in-chat commands:
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ From the repository root:
|
||||
```sh
|
||||
cd cassady-desktop
|
||||
npm install
|
||||
cargo tauri build --target aarch64-apple-darwin
|
||||
cargo tauri build --target aarch64-apple-darwin --no-bundle -- --locked
|
||||
```
|
||||
|
||||
`cargo tauri build` runs the frontend build and embeds the generated assets into the standalone binary. A plain `cargo build -p cassady-desktop` is useful for Rust checks, but do not package that output because it may run in Tauri dev-asset mode and open to a blank window when copied elsewhere.
|
||||
`cargo tauri build` runs the frontend build and embeds the generated assets into the standalone binary. Release Cargo builds now fail fast when `cassady-desktop/dist` is missing or incomplete, and `cass desktop` rejects desktop binaries that do not contain the bundled frontend assets. A plain debug `cargo build -p cassady-desktop` is useful for Rust checks, but do not package that output.
|
||||
|
||||
For a GUI dev session, run the Vite dev server and the Tauri app using your local Tauri workflow. Release packaging uses the raw `cassady-desktop` binary, not DMG, app bundle, AppImage, MSI, or other installer artifacts.
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cassady-desktop-frontend",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cassady-desktop-frontend",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"dependencies": {
|
||||
"@fontsource/ibm-plex-mono": "^5.2.7",
|
||||
"@fontsource/ibm-plex-sans": "^5.2.8",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cassady-desktop-frontend",
|
||||
"private": true,
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cassady-desktop"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
edition = "2021"
|
||||
description = "Cassady desktop app"
|
||||
license = "MIT"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
// Tauri embeds the built frontend into release binaries. `cargo tauri build`
|
||||
// runs the frontend build before Cargo, so make Cargo rerun this build script
|
||||
@@ -11,5 +15,68 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed=../vite.config.ts");
|
||||
println!("cargo:rerun-if-changed=../package.json");
|
||||
println!("cargo:rerun-if-changed=../package-lock.json");
|
||||
|
||||
if env::var("PROFILE").as_deref() == Ok("release") {
|
||||
validate_release_build_mode();
|
||||
validate_frontend_dist();
|
||||
}
|
||||
|
||||
tauri_build::build()
|
||||
}
|
||||
|
||||
fn validate_release_build_mode() {
|
||||
if env::var("DEP_TAURI_DEV").as_deref() == Ok("false") {
|
||||
return;
|
||||
}
|
||||
|
||||
panic!(
|
||||
"Cassady desktop release binaries must be built with `cargo tauri build --no-bundle` so Tauri enables custom-protocol asset embedding. Plain `cargo build --release -p cassady-desktop` builds a dev-url binary that opens as a blank window when the Vite server is not running."
|
||||
);
|
||||
}
|
||||
|
||||
fn validate_frontend_dist() {
|
||||
let index = Path::new("../dist/index.html");
|
||||
let assets_dir = Path::new("../dist/assets");
|
||||
|
||||
if !index.is_file() {
|
||||
panic!(
|
||||
"Cassady desktop release builds require built frontend assets. Run `npm install` and `npm run build` in cassady-desktop, or use `cargo tauri build --no-bundle`, before building the desktop binary."
|
||||
);
|
||||
}
|
||||
|
||||
let html = fs::read_to_string(index).unwrap_or_else(|error| {
|
||||
panic!("failed to read {}: {error}", index.display());
|
||||
});
|
||||
if !html.contains("assets/index-") {
|
||||
panic!(
|
||||
"{} does not reference the Vite-built frontend assets. Run `npm run build` in cassady-desktop before building the release desktop binary.",
|
||||
index.display()
|
||||
);
|
||||
}
|
||||
|
||||
if !assets_dir.is_dir() {
|
||||
panic!(
|
||||
"{} is missing. Run `npm run build` in cassady-desktop before building the release desktop binary.",
|
||||
assets_dir.display()
|
||||
);
|
||||
}
|
||||
|
||||
let mut has_js = false;
|
||||
let mut has_css = false;
|
||||
for entry in fs::read_dir(assets_dir).unwrap_or_else(|error| {
|
||||
panic!("failed to read {}: {error}", assets_dir.display());
|
||||
}) {
|
||||
let path = entry
|
||||
.unwrap_or_else(|error| panic!("failed to read asset entry: {error}"))
|
||||
.path();
|
||||
has_js |= path.extension().and_then(|extension| extension.to_str()) == Some("js");
|
||||
has_css |= path.extension().and_then(|extension| extension.to_str()) == Some("css");
|
||||
}
|
||||
|
||||
if !has_js || !has_css {
|
||||
panic!(
|
||||
"{} must contain Vite-built JavaScript and CSS assets. Run `npm run build` in cassady-desktop before building the release desktop binary.",
|
||||
assets_dir.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Cassady",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"identifier": "com.cassady.desktop",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ Runs the interactive setup wizard in a terminal. It configures providers, authen
|
||||
|
||||
### `cass desktop`
|
||||
|
||||
Launches the Cassady desktop app and immediately detaches it from the terminal, so the app window can keep running after you close the terminal. The desktop binary must be installed as `cassady-desktop` next to `cass` or somewhere on `PATH`. You can also set `CASSADY_DESKTOP_BIN=/path/to/cassady-desktop`.
|
||||
Launches the Cassady desktop app and immediately detaches it from the terminal, so the app window can keep running after you close the terminal. The desktop binary must be installed as `cassady-desktop` next to `cass` or somewhere on `PATH`, and it must be a release-built binary with the bundled frontend assets. Cassady rejects assetless desktop binaries because they open as a blank window. You can also set `CASSADY_DESKTOP_BIN=/path/to/cassady-desktop`.
|
||||
|
||||
Useful option:
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ That directory contains `config.json`, `providers.json`, `models.json`, `global.
|
||||
|
||||
## Release artifacts and updates
|
||||
|
||||
When using release archives, each archive contains both `cass` and `cassady`. macOS and Linux archives also contain `cassady-desktop`; put all extracted binaries somewhere on your `PATH` or keep them in the same directory so `cass desktop` can launch the desktop app.
|
||||
When using release archives, each archive contains both `cass` and `cassady`. macOS and Linux archives also contain a release-built `cassady-desktop` with bundled frontend assets; put all extracted binaries somewhere on your `PATH` or keep them in the same directory so `cass desktop` can launch the desktop app.
|
||||
|
||||
`cass update` can update release-archive installs from official GitHub releases. It supports the same prebuilt targets as the release process:
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
## Cassady v0.4.1
|
||||
|
||||
Cassady v0.4.1 fixes the desktop launch path so `cass desktop` no longer opens a blank white window when an assetless or stale `cassady-desktop` binary is found.
|
||||
|
||||
### Downloads
|
||||
|
||||
- macOS Apple Silicon: `cassady-v0.4.1-aarch64-apple-darwin.tar.gz`
|
||||
- Linux x86_64: `cassady-v0.4.1-x86_64-unknown-linux-gnu.tar.gz`
|
||||
- Linux ARM64: `cassady-v0.4.1-aarch64-unknown-linux-gnu.tar.gz`
|
||||
- Windows x86_64: `cassady-v0.4.1-x86_64-pc-windows-gnu.zip`
|
||||
|
||||
The macOS and Linux archives contain `cass`, `cassady`, and `cassady-desktop`; launch the desktop app with `cass desktop`. The Windows archive contains `cass` and `cassady`. SHA-256 checksum files are included for every archive.
|
||||
|
||||
### Highlights
|
||||
|
||||
- Rejects stale or incorrectly built `cassady-desktop` binaries that do not contain the bundled frontend assets, replacing a blank window with an actionable error.
|
||||
- Fails release desktop builds early unless they are built through the Tauri release path with embedded frontend assets.
|
||||
- Updates desktop build and install documentation to make the required release-built companion binary explicit.
|
||||
|
||||
### Upgrade notes
|
||||
|
||||
No config migration is required. If `cass desktop` previously opened a blank white window, update both `cass` and the companion `cassady-desktop` binary from the v0.4.1 macOS/Linux archive, or reinstall the npm package once v0.4.1 is published.
|
||||
|
||||
### Install from source
|
||||
|
||||
```sh
|
||||
cargo install --git https://github.com/owenqwenstarsky/cassady --tag v0.4.1
|
||||
```
|
||||
|
||||
### Verification
|
||||
|
||||
Built and tested with the GitHub Actions release workflow:
|
||||
|
||||
```sh
|
||||
cargo test --locked --all-targets
|
||||
cargo build -p cassady --release --locked --target aarch64-apple-darwin --bin cass --bin cassady
|
||||
(cd cassady-desktop && cargo tauri build --target aarch64-apple-darwin --no-bundle -- --locked)
|
||||
cargo build -p cassady --release --locked --target x86_64-unknown-linux-gnu --bin cass --bin cassady
|
||||
(cd cassady-desktop && cargo tauri build --target x86_64-unknown-linux-gnu --no-bundle -- --locked)
|
||||
cargo build -p cassady --release --locked --target aarch64-unknown-linux-gnu --bin cass --bin cassady
|
||||
(cd cassady-desktop && cargo tauri build --target aarch64-unknown-linux-gnu --no-bundle -- --locked)
|
||||
cargo zigbuild -p cassady --release --locked --target x86_64-pc-windows-gnu --bin cass --bin cassady
|
||||
```
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::cli::{Cli, DesktopArgs};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
const DESKTOP_BIN_ENV: &str = "CASSADY_DESKTOP_BIN";
|
||||
const DESKTOP_FRONTEND_ASSET_MARKER: &[u8] = b"assets/index-";
|
||||
|
||||
pub fn launch(cli: &Cli, args: &DesktopArgs) -> Result<()> {
|
||||
let cwd = resolve_launch_cwd(cli.cwd.as_deref())?;
|
||||
@@ -40,6 +42,7 @@ fn locate_desktop_binary() -> Result<PathBuf> {
|
||||
if let Some(path) = env::var_os(DESKTOP_BIN_ENV).filter(|value| !value.is_empty()) {
|
||||
let path = PathBuf::from(path);
|
||||
if is_runnable_file(&path) {
|
||||
validate_desktop_binary(&path)?;
|
||||
return Ok(path);
|
||||
}
|
||||
bail!(
|
||||
@@ -53,12 +56,14 @@ fn locate_desktop_binary() -> Result<PathBuf> {
|
||||
if let Some(dir) = current_exe.parent() {
|
||||
let candidate = dir.join(binary_name);
|
||||
if is_runnable_file(&candidate) {
|
||||
validate_desktop_binary(&candidate)?;
|
||||
return Ok(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path) = find_on_path(binary_name) {
|
||||
validate_desktop_binary(&path)?;
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
@@ -82,6 +87,25 @@ fn find_on_path(name: &str) -> Option<PathBuf> {
|
||||
.find(|candidate| is_runnable_file(candidate))
|
||||
}
|
||||
|
||||
fn validate_desktop_binary(path: &Path) -> Result<()> {
|
||||
if has_embedded_frontend_assets(path)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
bail!(
|
||||
"found Cassady desktop binary at {}, but it does not include the built frontend assets and would open a blank window. Rebuild it with `(cd cassady-desktop && cargo tauri build --no-bundle)` or install a macOS/Linux release/npm package that includes `cassady-desktop`.",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
|
||||
fn has_embedded_frontend_assets(path: &Path) -> Result<bool> {
|
||||
let bytes = fs::read(path)
|
||||
.with_context(|| format!("checking embedded desktop assets in {}", path.display()))?;
|
||||
Ok(bytes
|
||||
.windows(DESKTOP_FRONTEND_ASSET_MARKER.len())
|
||||
.any(|window| window == DESKTOP_FRONTEND_ASSET_MARKER))
|
||||
}
|
||||
|
||||
fn is_runnable_file(path: &Path) -> bool {
|
||||
path.is_file() && is_executable(path)
|
||||
}
|
||||
@@ -157,4 +181,18 @@ mod tests {
|
||||
let name = format!("definitely-not-cassady-desktop-{}", std::process::id());
|
||||
assert!(find_on_path(&name).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embedded_frontend_asset_marker_is_detected() {
|
||||
let file = tempfile::NamedTempFile::new().unwrap();
|
||||
std::fs::write(file.path(), b"prefix /assets/index-AbCdEf.js suffix").unwrap();
|
||||
assert!(has_embedded_frontend_assets(file.path()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_embedded_frontend_asset_marker_is_rejected() {
|
||||
let file = tempfile::NamedTempFile::new().unwrap();
|
||||
std::fs::write(file.path(), b"plain cargo-built desktop binary").unwrap();
|
||||
assert!(!has_embedded_frontend_assets(file.path()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user