fix(agents): import standalone JRE offline packages

This commit is contained in:
zipg authored and GitHub committed 2026-09-15 18:09:01 +08:00
1 parent 2fd8f42246
commit 168f854f8a
20 files changed
+306 -13

No files matched your search

@@ -744,9 +744,9 @@ async function importOfflineZip() {
activeAgentOperationId.value = uuid();
resetAgentInstallProgress();
try {
const count = await api.importAgentsFromZip(selected, activeAgentOperationId.value);
const result = await api.importAgentsFromZip(selected, activeAgentOperationId.value);
await Promise.all([refreshAgents(), loadJdbcDrivers(), loadJdbcPluginStatus()]);
toast(t("driverStore.offlineImportSuccess", { count }));
toast(t(result.jreCount > 0 ? (result.count > 0 ? "driverStore.offlineImportWithJreSuccess" : "driverStore.offlineJreImportSuccess") : "driverStore.offlineImportSuccess", { count: result.count, jreCount: result.jreCount }));
} catch (e: any) {
toast(t("driverStore.offlineImportFailed", { error: backendError(e) }));
} finally {
@@ -776,9 +776,9 @@ async function importDriverFile(driver: AgentDriverInfo) {
activeAgentOperationId.value = uuid();
resetAgentInstallProgress();
try {
const count = await api.importAgentsFromZip(selected, activeAgentOperationId.value);
const result = await api.importAgentsFromZip(selected, activeAgentOperationId.value);
await Promise.all([refreshAgents(), loadJdbcDrivers(), loadJdbcPluginStatus()]);
toast(t("driverStore.offlineImportSuccess", { count }));
toast(t(result.jreCount > 0 ? (result.count > 0 ? "driverStore.offlineImportWithJreSuccess" : "driverStore.offlineJreImportSuccess") : "driverStore.offlineImportSuccess", { count: result.count, jreCount: result.jreCount }));
} finally {
activeAgentOperationId.value = null;
resetAgentInstallProgress();
+2
View File
@@ -7861,6 +7861,8 @@ export default withEnglishFallback({
driverUninstallFailed: "{label} drayverini silmək mümkün olmadı: {error}",
chooseOfflineDriverPackage: "Oflayn drayver paketini seç",
offlineImportSuccess: "Oflayn idxal tamamlandı. {count} drayver quraşdırıldı.",
offlineJreImportSuccess: "Oflayn idxal tamamlandı. {jreCount} JRE quraşdırıldı.",
offlineImportWithJreSuccess: "Oflayn idxal tamamlandı. {jreCount} JRE və {count} drayver quraşdırıldı.",
offlineImportFailed: "Oflayn idxal uğursuz oldu: {error}",
driverImportSuccess: "{label} drayveri idxal edildi",
driverImportFailed: "{label} drayverini idxal etmək mümkün olmadı: {error}",
+2
View File
@@ -8051,6 +8051,8 @@ export default {
driverUninstallFailed: "Failed to uninstall {label} driver: {error}",
chooseOfflineDriverPackage: "Choose offline driver package",
offlineImportSuccess: "Offline import complete. Installed {count} driver(s).",
offlineJreImportSuccess: "Offline import complete. Installed {jreCount} JRE(s).",
offlineImportWithJreSuccess: "Offline import complete. Installed {jreCount} JRE(s) and {count} driver(s).",
offlineImportFailed: "Offline import failed: {error}",
driverImportSuccess: "{label} driver imported",
driverImportFailed: "Failed to import {label} driver: {error}",
+2
View File
@@ -7599,6 +7599,8 @@ export default withEnglishFallback({
driverUninstallFailed: "Error al desinstalar el driver {label}: {error}",
chooseOfflineDriverPackage: "Seleccionar paquete offline de drivers",
offlineImportSuccess: "Importación offline completada. Se instalaron {count} driver(s).",
offlineJreImportSuccess: "Importación offline completada. Se instalaron {jreCount} JRE(s).",
offlineImportWithJreSuccess: "Importación offline completada. Se instalaron {jreCount} JRE(s) y {count} driver(s).",
offlineImportFailed: "Error en la importación offline: {error}",
driverImportSuccess: "Driver {label} importado",
driverImportFailed: "Error al importar el driver {label}: {error}",
+2
View File
@@ -7599,6 +7599,8 @@ export default withEnglishFallback({
driverUninstallFailed: "Disinstillazione del driver {label} non riuscita: {error}",
chooseOfflineDriverPackage: "Scegli pacchetto driver offline",
offlineImportSuccess: "Importazione offline completata. Installato/i {count} driver.",
offlineJreImportSuccess: "Importazione offline completata. Installato/i {jreCount} JRE.",
offlineImportWithJreSuccess: "Importazione offline completata. Installato/i {jreCount} JRE e {count} driver.",
offlineImportFailed: "Importazione offline non riuscita: {error}",
driverImportSuccess: "Driver {label} importato",
driverImportFailed: "Importazione del driver {label} non riuscita: {error}",
+2
View File
@@ -7651,6 +7651,8 @@ export default withEnglishFallback({
driverUninstallFailed: "{label} ドライバーのアンインストールに失敗しました: {error}",
chooseOfflineDriverPackage: "オフラインドライバーパッケージを選択",
offlineImportSuccess: "オフラインインポートが完了しました。{count}件のドライバーをインストールしました。",
offlineJreImportSuccess: "オフラインインポートが完了しました。{jreCount}件のJREをインストールしました。",
offlineImportWithJreSuccess: "オフラインインポートが完了しました。{jreCount}件のJREと{count}件のドライバーをインストールしました。",
offlineImportFailed: "オフラインインポートに失敗しました: {error}",
driverImportSuccess: "{label} ドライバーをインポートしました",
driverImportFailed: "{label} ドライバーのインポートに失敗しました: {error}",
+2
View File
@@ -7331,6 +7331,8 @@ export default withEnglishFallback({
driverUninstallFailed: "{label} 드라이버 제거 실패: {error}",
chooseOfflineDriverPackage: "오프라인 드라이버 패키지 선택",
offlineImportSuccess: "오프라인 가져오기 완료. 드라이버 {count}개 설치.",
offlineJreImportSuccess: "오프라인 가져오기 완료. JRE {jreCount}개 설치.",
offlineImportWithJreSuccess: "오프라인 가져오기 완료. JRE {jreCount}개와 드라이버 {count}개 설치.",
offlineImportFailed: "오프라인 가져오기 실패: {error}",
driverImportSuccess: "{label} 드라이버를 가져왔습니다",
driverImportFailed: "{label} 드라이버 가져오기 실패: {error}",
+2
View File
@@ -7601,6 +7601,8 @@ export default withEnglishFallback({
driverUninstallFailed: "Falha ao desinstalar o driver {label}: {error}",
chooseOfflineDriverPackage: "Escolher pacote de driver offline",
offlineImportSuccess: "Importação offline concluída. {count} driver(s) instalado(s).",
offlineJreImportSuccess: "Importação offline concluída. {jreCount} JRE(s) instalado(s).",
offlineImportWithJreSuccess: "Importação offline concluída. {jreCount} JRE(s) e {count} driver(s) instalado(s).",
offlineImportFailed: "Falha na importação offline: {error}",
driverImportSuccess: "Driver {label} importado",
driverImportFailed: "Falha ao importar o driver {label}: {error}",
+2
View File
@@ -7713,6 +7713,8 @@ export default withEnglishFallback({
driverUninstallFailed: "{label} sürücüsü kaldırılamadı: {error}",
chooseOfflineDriverPackage: "Çevrimdışı sürücü paketini seçin",
offlineImportSuccess: "Çevrimdışı içe aktarma tamamlandı. {count} sürücü kuruldu.",
offlineJreImportSuccess: "Çevrimdışı içe aktarma tamamlandı. {jreCount} JRE kuruldu.",
offlineImportWithJreSuccess: "Çevrimdışı içe aktarma tamamlandı. {jreCount} JRE ve {count} sürücü kuruldu.",
offlineImportFailed: "Çevrimdışı içe aktarma başarısız: {error}",
driverImportSuccess: "{label} sürücüsü içe aktarıldı",
driverImportFailed: "{label} sürücüsü içe aktarılamadı: {error}",
+2
View File
@@ -8023,6 +8023,8 @@ export default withEnglishFallback({
driverUninstallFailed: "{label} 驱动卸载失败: {error}",
chooseOfflineDriverPackage: "选择离线驱动包",
offlineImportSuccess: "离线导入完成,已安装 {count} 个驱动",
offlineJreImportSuccess: "离线导入完成,已安装 {jreCount} 个 JRE",
offlineImportWithJreSuccess: "离线导入完成,已安装 {jreCount} 个 JRE 和 {count} 个驱动",
offlineImportFailed: "离线导入失败: {error}",
driverImportSuccess: "{label} 驱动导入成功",
driverImportFailed: "{label} 驱动导入失败: {error}",
+2
View File
@@ -6903,6 +6903,8 @@ export default withEnglishFallback({
driverUninstallFailed: "{label} 驅動程式解除安裝失敗: {error}",
chooseOfflineDriverPackage: "選擇離線驅動程式包",
offlineImportSuccess: "離線匯入完成,已安裝 {count} 個驅動程式",
offlineJreImportSuccess: "離線匯入完成,已安裝 {jreCount} 個 JRE",
offlineImportWithJreSuccess: "離線匯入完成,已安裝 {jreCount} 個 JRE 和 {count} 個驅動程式",
offlineImportFailed: "離線匯入失敗: {error}",
driverImportSuccess: "{label} 驅動程式匯入成功",
driverImportFailed: "{label} 驅動程式匯入失敗: {error}",
@@ -0,0 +1,37 @@
import { afterEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({ invoke: vi.fn() }));
vi.mock("@tauri-apps/api/core", () => ({ invoke: mocks.invoke }));
afterEach(() => {
vi.unstubAllGlobals();
vi.clearAllMocks();
});
describe("offline Agent and standalone JRE import", () => {
it("returns separate driver and JRE counts from the desktop command", async () => {
const { importAgentsFromZip } = await import("../tauri");
mocks.invoke.mockResolvedValue({ count: 0, jreCount: 1 });
expect(await importAgentsFromZip("/tmp/renamed.tar.zst", "import-1")).toEqual({ count: 0, jreCount: 1 });
expect(mocks.invoke).toHaveBeenCalledWith("import_agents_from_zip", { path: "/tmp/renamed.tar.zst", operationId: "import-1" });
});
it("uploads standalone JRE packages and preserves both result counts", async () => {
const fetchMock = vi.fn().mockResolvedValue(new Response(JSON.stringify({ count: 0, jreCount: 1 })));
vi.stubGlobal("fetch", fetchMock);
const { importAgentsFromZip } = await import("../http");
const file = new File(["archive"], "renamed.tar.zst");
expect(await importAgentsFromZip(file, "import-2")).toEqual({ count: 0, jreCount: 1 });
const [url, request] = fetchMock.mock.calls[0];
expect(url).toContain("/api/agents/import-offline");
expect(request.method).toBe("POST");
expect(request.body.get("operationId")).toBe("import-2");
expect(request.body.get("file").name).toBe(file.name);
});
it("remains compatible with web backends that only return a driver count", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response(JSON.stringify({ count: 2 }))));
const { importAgentsFromZip } = await import("../http");
expect(await importAgentsFromZip(new File(["archive"], "drivers.zip"))).toEqual({ count: 2, jreCount: 0 });
});
});
+4 -3
View File
@@ -69,6 +69,7 @@ import type {
DriverStoreUsage,
DriverRuntimeSummary,
UpgradeAllAgentDriversResult,
AgentOfflineImportResult,
AgentUpdateBlocker,
AgentOfflineExportPreview,
AgentOfflineExportResult,
@@ -861,7 +862,7 @@ export async function invalidateAgentRegistryCache(): Promise<void> {
await post("/api/agents/invalidate-registry-cache", {});
}
export async function importAgentsFromZip(fileOrPath: string | File, operationId?: string): Promise<number> {
export async function importAgentsFromZip(fileOrPath: string | File, operationId?: string): Promise<AgentOfflineImportResult> {
if (typeof fileOrPath === "string") {
throw new Error("Offline package import in web mode requires a File object, not a file path");
}
@@ -873,8 +874,8 @@ export async function importAgentsFromZip(fileOrPath: string | File, operationId
body: formData,
});
if (!res.ok) throw await backendResponseError(res);
const result: { count: number } = await res.json();
return result.count;
const result: AgentOfflineImportResult = await res.json();
return { count: result.count, jreCount: result.jreCount ?? 0 };
}
export async function previewAgentOfflineExport(): Promise<AgentOfflineExportPreview> {
+6 -1
View File
@@ -200,6 +200,11 @@ export interface AgentOfflineExportResult {
bytes: number;
}
export interface AgentOfflineImportResult {
count: number;
jreCount: number;
}
export type JavaRuntimeMode = "managed" | "system" | "custom";
export interface JavaRuntimeConfig {
@@ -2511,7 +2516,7 @@ export async function invalidateAgentRegistryCache(): Promise<void> {
return invoke("invalidate_agent_registry_cache");
}
export async function importAgentsFromZip(path: string | File, operationId?: string): Promise<number> {
export async function importAgentsFromZip(path: string | File, operationId?: string): Promise<AgentOfflineImportResult> {
if (typeof path !== "string") {
throw new Error("Desktop offline package import requires a local file path");
}