Idiomatic Kotlin over the same Rust core the CLI uses, through the Java Foreign Function & Memory API (JEP 454). Typed @Serializable data classes, an AutoCloseable client, CompletableFuture async and progress lambdas. Published as io.github.orelvis15:cavs-sdk.
Gradle and Maven. The published jar bundles the native library per platform, with a SHA-256 sidecar the loader verifies.
The native bridge uses the FFM API finalized in Java 22. Run with --enable-native-access=ALL-UNNAMED.
The FFM backend sits behind an interface, so an alternative bridge can slot in without touching CavsClient.
Released builds bundle the platform library in the jar. For a source checkout, build cavs-ffi and point CAVS_SDK_LIBRARY at it.
// build.gradle.kts dependencies { implementation("io.github.orelvis15:cavs-sdk:1.2.0") }
<!-- pom.xml --> <dependency> <groupId>io.github.orelvis15</groupId> <artifactId>cavs-sdk</artifactId> <version>1.2.0</version> </dependency>
import com.cavs.sdk.CavsClient
import com.cavs.sdk.model.PreviewRequest
CavsClient.create().use { cavs ->
val preview = cavs.preview(
PreviewRequest(oldPath = "Build_v1", newPath = "Build_v2", policy = "balanced"),
)
println("Recommended route: ${preview.recommendedRoute}")
preview.routes.forEach { println(" ${it.name}: ${it.networkBytes} bytes") }
}
Run with java --enable-native-access=ALL-UNNAMED ….
CavsClient is AutoCloseable. Every method takes a typed request and an optional (ProgressEvent) -> Unit lambda; previewAsync, createPlanAsync and applyPlanAsync return a CompletableFuture. Failures throw CavsException with a CavsErrorCode.
analyze(AnalyzeRequest)packDirectory(PackDirectoryRequest)preview(PreviewRequest)createPlan(CreatePlanRequest)applyPlan(ApplyPlanRequest)verifyInstall(VerifyRequest)benchmark(BenchmarkRequest)estimateSavings(SavingsRequest)CavsClient.create().use { cavs ->
// Async: returns a CompletableFuture.
val future = cavs.createPlanAsync(
CreatePlanRequest(oldPath = "Build_v1", newPath = "Build_v2", outputPlan = "update.cavsplan"),
)
// Sync with a progress lambda.
val plan = cavs.createPlan(
CreatePlanRequest(oldPath = "Build_v1", newPath = "Build_v2", outputPlan = "update.cavsplan"),
) { e -> println("[${e.type}] ${e.phase}") }
try {
cavs.analyze(AnalyzeRequest(oldPath = "missing", newPath = "Build_v2"))
} catch (e: CavsException) {
if (e.code == CavsErrorCode.PATH_NOT_FOUND) println("old build not found")
}
}
// Jenkins / Gradle — fail the build when the update is too large
pipeline {
agent any
stages {
stage('CAVS Preview') {
steps { sh './gradlew runCavsPreview' }
}
}
}
The reports are Kotlin data classes serialized with kotlinx.serialization — marshal them to JSON for dashboards, or assert on estimatedUpdateBytes to gate a release.