From 7f23e38f96a320318634e4e65c823a3810dd92f4 Mon Sep 17 00:00:00 2001 From: hbc Date: Mon, 19 Jan 2026 15:09:42 -0800 Subject: [PATCH 1/9] fix: bump to v1 --- .github/workflows/release-standalone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-standalone.yml b/.github/workflows/release-standalone.yml index bc34a38..b07b5bc 100644 --- a/.github/workflows/release-standalone.yml +++ b/.github/workflows/release-standalone.yml @@ -48,7 +48,7 @@ jobs: working-directory: absurd-sqlite-extension - name: Build and release - uses: tauri-apps/tauri-action@v0 + uses: tauri-apps/tauri-action@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Use ad-hoc signing on macOS by setting identity to '-' per Tauri v2 docs: From 5cd7c4c5a2c8ee02612033d871f20c878eb7f43c Mon Sep 17 00:00:00 2001 From: hbc Date: Mon, 19 Jan 2026 15:17:07 -0800 Subject: [PATCH 2/9] feat: download extension from remote release --- Cargo.lock | 204 +++++++++++++++++++++++++++++++- standalone/src-tauri/Cargo.toml | 1 + standalone/src-tauri/src/db.rs | 150 +++++++++++++++++++---- 3 files changed, 334 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4afc89a..bc56f11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,7 @@ dependencies = [ "chrono", "libc", "log", + "reqwest", "rusqlite", "serde", "serde_json", @@ -445,7 +446,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "which", ] @@ -1515,6 +1516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1729,8 +1731,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1740,9 +1744,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -2058,6 +2064,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-util" version = "0.1.19" @@ -2544,6 +2567,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "mac" version = "0.1.1" @@ -3513,6 +3542,61 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash 2.1.1", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.42" @@ -3756,22 +3840,28 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", + "futures-channel", "futures-core", "futures-util", "http", "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http 0.6.8", @@ -3781,6 +3871,21 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", ] [[package]] @@ -3849,6 +3954,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3884,6 +3995,41 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -4426,6 +4572,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swift-rs" version = "1.0.7" @@ -5079,6 +5231,16 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -5386,6 +5548,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.7" @@ -5612,6 +5780,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webkit2gtk" version = "2.0.1" @@ -5656,6 +5834,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webview2-com" version = "0.38.0" @@ -5898,6 +6085,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -6369,6 +6565,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.2.3" diff --git a/standalone/src-tauri/Cargo.toml b/standalone/src-tauri/Cargo.toml index 826b58a..8e96a5d 100644 --- a/standalone/src-tauri/Cargo.toml +++ b/standalone/src-tauri/Cargo.toml @@ -25,6 +25,7 @@ log = "0.4.29" rusqlite = {version = "0.38.0", features = ["bundled", "load_extension"] } serde = {version = "1", features = ["derive"] } serde_json = "1" +reqwest = {version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] } tauri = {version = "2", features = ["tray-icon", "image-png"] } tauri-plugin-fs = "2" tauri-plugin-log = "2" diff --git a/standalone/src-tauri/src/db.rs b/standalone/src-tauri/src/db.rs index e145ad1..bbcf908 100644 --- a/standalone/src-tauri/src/db.rs +++ b/standalone/src-tauri/src/db.rs @@ -1,6 +1,10 @@ use anyhow::{Context, Result}; +use reqwest::blocking::Client; use rusqlite::Connection; +use serde::Deserialize; use serde_json::Value; +use std::env; +use std::fs; use std::path::{Path, PathBuf}; use tauri::{AppHandle, Manager}; use tauri_plugin_cli::ArgData; @@ -118,6 +122,17 @@ pub fn extension_path(app_handle: &AppHandle) -> Result { fn resolve_extension_path(app_handle: &AppHandle) -> Option { let lib_name = extension_lib_name(); + if let Ok(path) = env::var("ABSURD_SQLITE_EXTENSION_PATH") { + let path = PathBuf::from(path); + if path.exists() { + log::info!("Using SQLite extension from env at {}", path.display()); + return Some(path); + } + log::warn!( + "SQLite extension path from env does not exist: {}", + path.display() + ); + } #[cfg(debug_assertions)] { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -140,26 +155,9 @@ fn resolve_extension_path(app_handle: &AppHandle) -> Option { } } - match app_handle.path().resource_dir() { - Ok(resource_dir) => { - let resource_path = resource_dir.join("resources").join(&lib_name); - log::debug!( - "Checking resource SQLite extension at {}", - resource_path.display() - ); - if resource_path.exists() { - log::info!( - "Using resource SQLite extension at {}", - resource_path.display() - ); - return Some(resource_path); - } - log::warn!( - "SQLite extension not found in resources at {}", - resource_path.display() - ); - } - Err(err) => log::warn!("Failed to resolve resource directory: {}", err), + match download_extension(app_handle) { + Ok(path) => return Some(path), + Err(err) => log::warn!("Failed to download SQLite extension: {:#}", err), } #[cfg(debug_assertions)] @@ -192,3 +190,115 @@ fn extension_lib_name() -> String { "libabsurd.so".to_string() } } + +struct PlatformInfo { + os: &'static str, + arch: &'static str, + ext: &'static str, +} + +#[derive(Deserialize)] +struct ReleaseInfo { + tag_name: String, + draft: bool, +} + +fn download_extension(app_handle: &AppHandle) -> Result { + let owner = "b4fun"; + let repo = "absurd-sqlite"; + + let client = Client::builder() + .user_agent("absurd-sqlite-standalone") + .build() + .context("build GitHub HTTP client")?; + + let version = fetch_latest_version(&client, owner, repo)?; + let platform = platform_info()?; + let asset_name = asset_name(&version, &platform); + let tag = format!("absurd-sqlite-extension/{}", version); + + let cache_dir = app_handle + .path() + .app_cache_dir() + .context("resolve app cache dir")? + .join("absurd-sqlite") + .join("extensions") + .join(&version); + fs::create_dir_all(&cache_dir).context("create extension cache dir")?; + + let cached_path = cache_dir.join(extension_lib_name()); + if cached_path.exists() { + log::info!("Using cached SQLite extension at {}", cached_path.display()); + return Ok(cached_path); + } + + let url = format!("https://github.com/{owner}/{repo}/releases/download/{tag}/{asset_name}"); + log::info!("Downloading SQLite extension from {}", url); + let response = client + .get(url) + .send() + .context("request extension asset")? + .error_for_status() + .context("download extension asset")?; + let bytes = response.bytes().context("read extension bytes")?; + fs::write(&cached_path, bytes).context("write extension to cache")?; + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let permissions = fs::Permissions::from_mode(0o755); + fs::set_permissions(&cached_path, permissions).context("chmod extension")?; + } + + Ok(cached_path) +} + +fn fetch_latest_version(client: &Client, owner: &str, repo: &str) -> Result { + let url = format!("https://api.github.com/repos/{owner}/{repo}/releases"); + let releases = client + .get(url) + .send() + .context("fetch releases")? + .error_for_status() + .context("download releases")? + .json::>() + .context("parse releases")?; + + for release in releases { + if !release.draft && release.tag_name.starts_with("absurd-sqlite-extension/") { + return Ok(release + .tag_name + .trim_start_matches("absurd-sqlite-extension/") + .to_string()); + } + } + + Err(anyhow::anyhow!("No extension releases found")) +} + +fn platform_info() -> Result { + let (os, ext) = if cfg!(target_os = "macos") { + ("macOS", "dylib") + } else if cfg!(target_os = "linux") { + ("Linux", "so") + } else if cfg!(target_os = "windows") { + ("Windows", "dll") + } else { + return Err(anyhow::anyhow!("Unsupported platform")); + }; + + let arch = match env::consts::ARCH { + "x86_64" => "X64", + "aarch64" => "ARM64", + other => return Err(anyhow::anyhow!("Unsupported architecture: {}", other)), + }; + + Ok(PlatformInfo { os, arch, ext }) +} + +fn asset_name(version: &str, platform: &PlatformInfo) -> String { + format!( + "absurd-absurd-sqlite-extension-{}-{}-{}.{}", + version, platform.os, platform.arch, platform.ext + ) +} From 9c334432742c31588cc69601e9020cec20a084a9 Mon Sep 17 00:00:00 2001 From: hbc Date: Mon, 19 Jan 2026 15:20:18 -0800 Subject: [PATCH 3/9] Revert "fix: bump to v1" This reverts commit 7f23e38f96a320318634e4e65c823a3810dd92f4. --- .github/workflows/release-standalone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-standalone.yml b/.github/workflows/release-standalone.yml index b07b5bc..bc34a38 100644 --- a/.github/workflows/release-standalone.yml +++ b/.github/workflows/release-standalone.yml @@ -48,7 +48,7 @@ jobs: working-directory: absurd-sqlite-extension - name: Build and release - uses: tauri-apps/tauri-action@v1 + uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Use ad-hoc signing on macOS by setting identity to '-' per Tauri v2 docs: From eb322b3de17f4c185ff390e295bb240e3d71afea Mon Sep 17 00:00:00 2001 From: hbc Date: Mon, 19 Jan 2026 15:40:25 -0800 Subject: [PATCH 4/9] style: tune style --- .../src/lib/components/JsonBlock.svelte | 2 +- standalone/src/routes/tasks/+page.svelte | 66 +++++++++++++++---- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/standalone/src/lib/components/JsonBlock.svelte b/standalone/src/lib/components/JsonBlock.svelte index 659ebd3..8a00752 100644 --- a/standalone/src/lib/components/JsonBlock.svelte +++ b/standalone/src/lib/components/JsonBlock.svelte @@ -41,7 +41,7 @@ }; -
+
{title}