Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d49c04c
feat: add generic download module with timeout support
AnnChord Jun 22, 2026
d300703
feat: integrate download module into JDBC bridge
AnnChord Jun 22, 2026
77030d5
feat: register download module and extend JDBC commands
AnnChord Jun 22, 2026
198f7c1
feat: add progress event emission to Java JDBC bridge
AnnChord Jun 22, 2026
ccf63a5
feat: add download event composable for progress tracking
AnnChord Jun 22, 2026
6f8b436
feat: wire download progress into connection dialog and JRE settings
AnnChord Jun 22, 2026
61c2fb4
chore: remove unused dependencies from lockfile
AnnChord Jun 22, 2026
de7bc03
Merge branch 'master' into feat/unified-download-progress
AnnChord Jun 22, 2026
86b95f8
feat: add driver check-update backend command and driver mappings
AnnChord Jun 22, 2026
22935d2
feat: add DriverUpdateStatus type and checkDriverUpdate API method
AnnChord Jun 22, 2026
af785a7
fix: resolve driver download for non-Maven and version-pinned drivers
AnnChord Jun 22, 2026
16c4f72
feat: add i18n keys for JRE/driver check-update notifications
AnnChord Jun 22, 2026
47efba0
feat: add check-update UI for JRE and drivers in settings
AnnChord Jun 22, 2026
72a4034
feat: add app updater download progress with circular indicator
AnnChord Jun 22, 2026
60a5542
chore: apply rustfmt to commands and connection modules
AnnChord Jun 22, 2026
69b8fcf
chore: apply rustfmt to connection and database core modules
AnnChord Jun 22, 2026
15bbd7f
chore: apply rustfmt to capabilities, ssh, and other modules
AnnChord Jun 22, 2026
3ae13a5
chore: apply rustfmt to jdbc_bridge module
AnnChord Jun 22, 2026
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
5 changes: 4 additions & 1 deletion jdbc-bridge/src/main/java/sqlkit/bridge/BridgeMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
public class BridgeMain {

private static final ObjectMapper MAPPER = new ObjectMapper();
private static final ProtocolHandler HANDLER = new ProtocolHandler(new ConnectionManager());
private static final ProtocolHandler HANDLER = new ProtocolHandler(
new ConnectionManager(),
progressJson -> System.out.println(progressJson)
);

public static void main(String[] args) throws Exception {
if (args.length > 0 && "--version".equals(args[0])) {
Expand Down
60 changes: 49 additions & 11 deletions jdbc-bridge/src/main/java/sqlkit/bridge/DriverResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,20 @@ private static String getDriversCacheDir() {
* @param versionCap Optional max version to cap against. Null means resolve LATEST.
* @param classifier Optional Maven classifier (e.g. "standalone"). Null means no classifier.
* @param downloadUrl Direct download URL for drivers NOT on Maven Central. Null means use Maven.
* @param progressCb Callback invoked with (downloaded, total) during JAR download, or null.
* @return DriverResult with path to the cached JAR and resolved version
*/
public static DriverResult resolve(String mavenGroup, String mavenArtifact,
String versionCap, String classifier,
String downloadUrl) throws Exception {
String downloadUrl, ProgressCallback progressCb) throws Exception {
if (downloadUrl != null && !downloadUrl.isEmpty()) {
return resolveDirect(mavenArtifact, versionCap, downloadUrl);
return resolveDirect(mavenArtifact, versionCap, downloadUrl, progressCb);
}
return resolveFromMaven(mavenGroup, mavenArtifact, versionCap, classifier);
return resolveFromMaven(mavenGroup, mavenArtifact, versionCap, classifier, progressCb);
}

private static DriverResult resolveDirect(String mavenArtifact, String versionCap, String downloadUrl) throws Exception {
private static DriverResult resolveDirect(String mavenArtifact, String versionCap,
String downloadUrl, ProgressCallback progressCb) throws Exception {
String version = (versionCap != null && !versionCap.isEmpty()) ? versionCap : "1.0.0";
String jarFilename = mavenArtifact + "-" + version + ".jar";
Path destPath = Paths.get(DRIVERS_CACHE, mavenArtifact, jarFilename);
Expand All @@ -67,16 +69,30 @@ private static DriverResult resolveDirect(String mavenArtifact, String versionCa
if (!response.isSuccessful()) {
throw new Exception("Failed to download JAR: HTTP " + response.code() + " for " + downloadUrl);
}
byte[] jarBytes = response.body() != null ? response.body().bytes() : new byte[0];
response.close();
Files.write(destPath, jarBytes);
if (response.body() != null) {
long contentLength = response.body().contentLength();
long totalBytes = contentLength > 0 ? contentLength : 5_000_000L;
long downloadedBytes = 0L;
try (InputStream in = new BufferedInputStream(response.body().byteStream());
OutputStream out = Files.newOutputStream(destPath)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
downloadedBytes += bytesRead;
if (progressCb != null)
progressCb.onProgress(downloadedBytes, totalBytes);
}
}
}
}

return new DriverResult(destPath.toAbsolutePath().toString(), version);
}

private static DriverResult resolveFromMaven(String mavenGroup, String mavenArtifact,
String versionCap, String classifier) throws Exception {
String versionCap, String classifier,
ProgressCallback progressCb) throws Exception {
// 1. Fetch maven-metadata.xml
String metadataUrl = String.format("%s/%s/%s/maven-metadata.xml",
MAVEN_CENTRAL, mavenGroup.replace('.', '/'), mavenArtifact);
Expand Down Expand Up @@ -135,11 +151,26 @@ private static DriverResult resolveFromMaven(String mavenGroup, String mavenArti
if (!response.isSuccessful()) {
throw new Exception("Failed to download JAR: HTTP " + response.code() + " for " + downloadUrl);
}
if (response.body() == null) {
throw new Exception("Empty response body when downloading JAR from " + downloadUrl);
}

byte[] jarBytes = response.body() != null ? response.body().bytes() : new byte[0];
response.close();
long contentLength = response.body().contentLength();
long totalBytes = contentLength > 0 ? contentLength : 5_000_000L;
long downloadedBytes = 0L;

Files.write(destPath, jarBytes);
try (InputStream in = new BufferedInputStream(response.body().byteStream());
OutputStream out = Files.newOutputStream(destPath)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
downloadedBytes += bytesRead;
if (progressCb != null) {
progressCb.onProgress(downloadedBytes, totalBytes);
}
}
}
}

return new DriverResult(destPath.toAbsolutePath().toString(), latestVersion);
Expand Down Expand Up @@ -194,6 +225,13 @@ private static int tryParseInt(String s) {
}
}

/**
* Callback interface for tracking JAR download progress.
*/
public interface ProgressCallback {
void onProgress(long downloaded, long total);
}

/**
* Result of a driver resolution.
*/
Expand Down
20 changes: 18 additions & 2 deletions jdbc-bridge/src/main/java/sqlkit/bridge/ProtocolHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;

/**
* Dispatches JSON-RPC requests to the appropriate handler.
Expand All @@ -19,9 +20,11 @@ public class ProtocolHandler {
private static final ObjectMapper MAPPER = new ObjectMapper();

private final ConnectionManager connectionManager;
private final Consumer<String> progressConsumer;

public ProtocolHandler(ConnectionManager connectionManager) {
public ProtocolHandler(ConnectionManager connectionManager, Consumer<String> progressConsumer) {
this.connectionManager = connectionManager;
this.progressConsumer = progressConsumer;
}

/**
Expand Down Expand Up @@ -222,7 +225,20 @@ private void handleResolveDriver(JsonNode params, ObjectNode response) throws Ex
String downloadUrl = params.has("download_url") && !params.get("download_url").isNull()
? params.get("download_url").asText() : null;

DriverResolver.DriverResult result = DriverResolver.resolve(mavenGroup, mavenArtifact, versionCap, classifier, downloadUrl);
DriverResolver.ProgressCallback progressCb = (downloaded, total) -> {
try {
ObjectNode progressNode = MAPPER.createObjectNode();
progressNode.put("phase", "progress");
progressNode.put("downloaded", downloaded);
progressNode.put("total", total);
progressConsumer.accept(MAPPER.writeValueAsString(progressNode));
} catch (Exception e) {
// Ignore progress reporting errors
}
};

DriverResolver.DriverResult result = DriverResolver.resolve(
mavenGroup, mavenArtifact, versionCap, classifier, downloadUrl, progressCb);

ObjectNode resultNode = MAPPER.createObjectNode();
resultNode.put("jar_path", result.getJarPath());
Expand Down
20 changes: 0 additions & 20 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 src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ http = "1"
log = "0.4"
futures = "0.3"
rand = "0.8"
data-studio-agent = { path = "/Users/blankll/Documents/devs/geekfun/data-studio-agent" }
data-studio-agent = { path = "../../data-studio-agent" }

# Archive extraction (JRE downloads)
flate2 = "1.0"
Expand Down
11 changes: 6 additions & 5 deletions src-tauri/src/agent_adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use std::collections::HashMap;
use std::sync::Arc;

use data_studio_agent as lib;
use data_studio_agent::storage;
use data_studio_agent::traits::{CancelMap, ConfirmMap, EventEmitter};
use data_studio_agent::storage as storage;
use serde_json::Value;
use tauri::{AppHandle, Emitter, Manager, State};

Expand Down Expand Up @@ -44,7 +44,8 @@ pub async fn run_agent_loop(
let confirm_map: ConfirmMap = confirm_state.inner().clone();
let cancel_state: State<CancelMap> = app.state::<CancelMap>();
let cancel_map: CancelMap = cancel_state.inner().clone();
let executor_state: State<Arc<dyn lib::ToolExecutor>> = app.state::<Arc<dyn lib::ToolExecutor>>();
let executor_state: State<Arc<dyn lib::ToolExecutor>> =
app.state::<Arc<dyn lib::ToolExecutor>>();
let executor: Arc<dyn lib::ToolExecutor> = executor_state.inner().clone();

let connections: HashMap<String, Value> = settings
Expand Down Expand Up @@ -158,8 +159,7 @@ pub async fn run_agent_step(
base_url: Option<String>,
) -> Result<String, String> {
let result = lib::harness::run_agent_step(
provider, model, messages, tools,
http_proxy, proxy_mode, api_key, base_url,
provider, model, messages, tools, http_proxy, proxy_mode, api_key, base_url,
)
.await?;

Expand All @@ -178,7 +178,8 @@ pub async fn validate_llm_config(
proxy_mode: Option<String>,
base_url: Option<String>,
) -> Result<bool, String> {
lib::harness::validate_llm_config(provider, api_key, model, http_proxy, proxy_mode, base_url).await
lib::harness::validate_llm_config(provider, api_key, model, http_proxy, proxy_mode, base_url)
.await
}

#[tauri::command]
Expand Down
Loading
Loading